2012-01-19 19 views
10

Jestem nowy w Scala i próbuję objąć głowę kontynuacjami Próbuję odtworzyć oświadczenie C# yield return.Kontynuacje i do zrozumienia - jaka jest niezgodność?

Po this post, napisałem poniższy kod:

package com.company.scalatest 

import scala.util.continuations._; 

object GenTest { 

    val gen = new Generator[Int] { 
    def produce = { 
     yieldValue(1) 
     yieldValue(2) 
     yieldValue(3) 
     yieldValue(42) 
    } 
    } 
    // Does not compile :(

    // val gen2 = new Generator[Int] { 
    // def produce = { 
    //  var ints = List(1, 2, 3, 42); 
    // 
    //  ints.foreach((theInt) => yieldValue(theInt)); 
    // } 
    // } 

    // But this works? 
    val gen3 = new Generator[Int] { 
    def produce = { 
     var ints = List(1, 2, 3, 42); 
     var i = 0; 
     while (i < ints.length) { 
     yieldValue(ints(i)); 
     i = i + 1; 
     } 
    } 
    } 

    def main(args: Array[String]): Unit = { 
    gen.foreach(println); 
    // gen2.foreach(println); 
    gen3.foreach(println); 
    } 
} 

abstract class Generator[E] { 

    var loopFn: (E => Unit) = null 

    def produce(): Unit @cps[Unit] 

    def foreach(f: => (E => Unit)): Unit = { 
    loopFn = f 
    reset[Unit, Unit](produce) 
    } 

    def yieldValue(value: E) = 
    shift { genK: (Unit => Unit) => 
     loopFn(value) 
     genK(()) 
    () 
    } 
} 

Jak widać, gen2 jest wypowiedziało się, ponieważ nie skompilować. Ponieważ mogę łatwo iterować zawartość listy używając pętli while (zob. gen3), oczekiwałem, że pętla foreach będzie działała równie dobrze.

Błąd kompilacji jest następujący:

no type parameters for method foreach: (f: Int => B)Unit exist so that 
it can be applied to arguments (Int => Unit @scala.util.continuations.cpsParam[Unit,Unit]) 
--- because --- 
argument expression's type is not compatible with formal parameter type; 
found : Int => Unit @scala.util.continuations.cpsParam[Unit,Unit] 
required: Int => ?B 

Dlaczego mam ten błąd i czy jest jakiś sposób, aby obejść to coś czystszego niż pętli while?

Dziękuję

+2

możliwy duplikat [Wdrażanie wydajności (zwrot plonów) za pomocą kontynuacji Scala] (http: // stackoverflow.com/questions/2201882/implementation-yield-yield-return-using-scala-continuations) –

+0

Nie szukam odpowiedzi, która mówi mi, jak odtworzyć "powrót plonów" za pomocą kontynuacji Scali. Szukam przyczyny, dla której "gen2" w moim przykładzie nie działa. "Zwrot z inwestycji" jest tylko kontekstem, w którym napotkałem ten problem. – GuiSim

+0

Ach, przepraszam. –

Odpowiedz

4

Najpierw spójrzmy na to, co będzie trzeba czekać gen2 skompilować.

object CpsConversions { 

    import scala.collection.IterableLike 
    import scala.util.continuations._ 

    implicit def cpsIterable[A, Repr](xs: IterableLike[A, Repr]) = new { 
    def cps = new { 
     def foreach[B](f: A => [email protected][Unit, Unit]): [email protected][Unit, Unit] = { 
     val it = xs.iterator 
     while(it.hasNext) f(it.next) 
     } 
    } 
    } 
} 

object GenTest { 

    import CpsConversions.cpsIterable 
    val gen2 = new Generator[Int] { 
    def produce = { 
     var ints = List(1, 2, 3, 42) 
     ints.cps.foreach((theInt) => yieldValue(theInt)) 
    } 
    } 

Teraz spójrzmy na to, co się dzieje. Oryginalny gen2 nie skompilować na następującej linii:

ints.foreach((theInt) => yieldValue(theInt)) 

Ponieważ rodzaj yieldValue zawiera @cpsParam adnotacji, wtyczka kontynuacje przekształca funkcję przekazany do metody foreach jednego typu:

Int => Unit @cpsParam[Unit,Unit] 

Droga w hierarchii List[Int], zobaczysz foreach zdefiniowaną jako:

foreach [U] (f: (Int) ⇒ U): Unit 

Jest to problem, ponieważ typy nie pasują, a Scala nie wie, jak uzyskać od Int => U do Int => Unit @cpsParam[Unit,Unit]. Aby to naprawić, dodałem wersję CPS foreach w niejawnej konwersji, do której można uzyskać dostęp, dzwoniąc pod numer cps pod dowolnym numerem IterableLike.

Byłoby bardzo miło, gdyby ten niejawna konwersja może być dokonane bez wyraźnej cps rozmowy, ale nie znalazłem sposób, aby kompilator Scala uznają przydatność takiego niejawna konwersja do pimp nowe foreach na liście . Może to mieć związek z kolejnością, w której kompilator używa wtyczki kontynuacji, ale wiem zbyt mało o tym procesie, aby mieć pewność.

To wszystko dobrze i dobrze dla foreach. Twoje pytanie wspomina o zrozumieniu, które będzie wymagało zdefiniowania jednego z filter, map lub flatMap (w zależności od tego, co się dzieje w twoim zrozumieniu). Zaimplementowałem je w linku w powyższym komentarzu, który rozszerza obiekt CpsConversions powyżej, aby umożliwić ogólne zrozumienie.