Wie könnte man C# yield return
mithilfe von Scala-Fortsetzungen implementieren? Ich würde gerne in der Lage sein, Scala Iterator
s im selben Stil zu schreiben. Ein Stich ist in den Kommentaren this Scala news post, aber es funktioniert nicht (versucht mit der Scala 2.8.0 Beta). Antworten in einem related question deuten darauf hin, dass dies möglich ist, aber obwohl ich seit einiger Zeit mit begrenzten Fortsetzungen spiele, kann ich nicht genau meinen Kopf darum drehen, wie man das macht.Implementieren von Ertrag (Rendite) mit Scala-Fortsetzungen
Antwort
Bevor wir fortfahren, müssen wir eine Infrastruktur aufbauen. Unten ist eine trampoline, die auf Iteration
Objekte funktioniert. Eine Iteration ist eine Berechnung, die entweder Yield
einen neuen Wert oder Done
sein kann.
sealed trait Iteration[+R]
case class Yield[+R](result: R, next:() => Iteration[R]) extends Iteration[R]
case object Done extends Iteration[Nothing]
def trampoline[R](body: => Iteration[R]): Iterator[R] = {
def loop(thunk:() => Iteration[R]): Stream[R] = {
thunk.apply match {
case Yield(result, next) => Stream.cons(result, loop(next))
case Done => Stream.empty
}
}
loop(() => body).iterator
}
Das Trampolin verwendet eine interne Schleife, die die Folge von Iteration
Objekte in eine Stream
dreht. Wir erhalten dann eine Iterator
durch Aufruf von iterator
auf das resultierende Stream-Objekt. Durch die Verwendung eines Stream
ist unsere Bewertung faul; Wir bewerten unsere nächste Iteration erst, wenn sie benötigt wird.
Das Trampolin kann verwendet werden, um einen Iterator direkt zu erstellen.
val itr1 = trampoline {
Yield(1,() => Yield(2,() => Yield(3,() => Done)))
}
for (i <- itr1) { println(i) }
Das ziemlich schrecklich ist zu schreiben, also lassen Sie uns begrenzt Fortsetzungen verwenden, um unsere Iteration
Objekte automatisch zu erstellen.
Wir verwenden die shift
und reset
Betreiber die Berechnung zu brechen in Iteration
s, dann trampoline
verwenden, um die Iteration
s in ein Iterator
einzuschalten.
import scala.continuations._
import scala.continuations.ControlContext.{shift,reset}
def iterator[R](body: => Unit @cps[Iteration[R],Iteration[R]]): Iterator[R] =
trampoline {
reset[Iteration[R],Iteration[R]] { body ; Done }
}
def yld[R](result: R): Unit @cps[Iteration[R],Iteration[R]] =
shift((k: Unit => Iteration[R]) => Yield(result,() => k(())))
Jetzt können wir unser Beispiel umschreiben.
val itr2 = iterator[Int] {
yld(1)
yld(2)
yld(3)
}
for (i <- itr2) { println(i) }
Viel besser!
Jetzt ist hier ein Beispiel aus der C# reference page für yield
, die einige erweiterte Verwendung zeigt. Die Typen können ein bisschen schwierig zu gewöhnen sein, aber es funktioniert alles.
def power(number: Int, exponent: Int): Iterator[Int] = iterator[Int] {
def loop(result: Int, counter: Int): Unit @cps[Iteration[Int],Iteration[Int]] = {
if (counter < exponent) {
yld(result)
loop(result * number, counter + 1)
}
}
loop(number, 0)
}
for (i <- power(2, 8)) { println(i) }
Ich würde gerne die sehen Ausgabe von scalac -print für Iterator, yld und die Zuweisung zu itr2. Könnte jemand mit dem Plugin das zur Antwort hinzufügen? – retronym
Ich habe gerade versucht, dies anzuwenden, also hatte ich den Code ausgeführt und praktisch. Siehe http://gist.github.com/297230 für die Ausgabe (scrollen Sie nach unten). – huynhjl
Ich würde 'Iterator' in' YldIterator' umbenennen oder so etwas, um Verwirrung zu vermeiden. :-) –
Ich habe es geschafft, einen Weg zu finden, dies zu tun, nach ein paar Stunden Spielzeit. Ich dachte, dass es einfacher wäre, meinen Kopf einzuwickeln als all die anderen Lösungen, die ich bisher gesehen habe, obwohl ich Richards und Miles' Lösungen sehr zu schätzen weiß.
def loopWhile(cond: =>Boolean)(body: =>(Unit @suspendable)): Unit @suspendable = {
if (cond) {
body
loopWhile(cond)(body)
}
}
class Gen {
var prodCont: Unit => Unit = { x: Unit => prod }
var nextVal = 0
def yld(i: Int) = shift { k: (Unit => Unit) => nextVal = i; prodCont = k }
def next = { prodCont(); nextVal }
def prod = {
reset {
// following is generator logic; can be refactored out generically
var i = 0
i += 1
yld(i)
i += 1
yld(i)
// scala continuations plugin can't handle while loops, so need own construct
loopWhile (true) {
i += 1
yld(i)
}
}
}
}
val it = new Gen
println(it.next)
println(it.next)
println(it.next)
Scala Fortsetzungen können While-Schleifen nicht behandeln? Autsch! –
In der Tat. :(Hoffentlich ist das ein work-in-progress, aber ich glaube, dass for-comprehensions definitiv nicht mit shift kompatibel sind, da das bedeuten würde, die map/foreach/etc auseinander zu reißen. – Yang
Nicht mehr. Cps-code aus while aufrufen Schleife ist schon seit einiger Zeit möglich, For-Comprehensions sind aber immer noch nicht unterstützt (ich glaube nicht, dass sie jemals Unterstützung bekommen) –
- 1. Wann wird Ertrag verwendet?
- 2. Unterschiedliche Ergebnisse von Rendite und Rendite
- 3. Python-Ertrag vs Ruby-Ertrag
- 4. Abbrechen Ertrag von Host
- 5. Rendite-Rückgabe mit Null
- 6. Proper Typenannotation von Python-Funktionen mit Ertrag
- 7. Kann ich die Rendite für IEnumerable-Funktionen in VB.NET implementieren?
- 8. Rückgabetyp von Scala für/Ertrag
- 9. foreach vs ForEach mit Ertrag
- 10. Rendite-Rückgabe in Java
- 11. mit Drucken innerhalb def mit Ertrag Aussage
- 12. Ist Rendite zurückkehrend?
- 13. Unterschied zwischen "Ertrag" von Tornado und "Ertrag von" asyncio in Mechanismus?
- 14. Rendite zurück eine Reihe von Elementen
- 15. Wie funktioniert GC mit IEnumerator und Ertrag?
- 16. "Ertrag von iterierbar" vs "Rückgabe iter (iterierbar)"
- 17. Ruby: ProC# Call vs Ertrag
- 18. Ertrag nur einmal pro Iteration
- 19. Wann ist 'Ertrag' wirklich nötig?
- 20. Verwenden von Ertrag und Rückgabe einer Liste von Fehler
- 21. Wenn Rendite nie eintritt, wird Null zurückgegeben?
- 22. Benutzerdefinierte Sammlung Implementieren von IEnumerable
- 23. Call Rendite Put() in einem Erfolg Callback
- 24. IRR (interne Rendite) UDF in TeraData
- 25. Kann JSON mit Rendite-Return "live" zurückgegeben werden?
- 26. Unterschied zwischen `Ertrag von $ Generator` und` Return $ Generator`?
- 27. Wie funktioniert diese Funktion mit einem "Ertrag" im Detail?
- 28. Kann Ertrag produzieren mehrere aufeinanderfolgende Generatoren?
- 29. Wie funktioniert dieses Lambda/Ertrag/Generator-Verständnis?
- 30. Ertrag und Standardfall || nicht ausgeben Standardfall
Was funktioniert bei diesem Beispiel nicht? Es kompiliert nicht, oder es produziert nicht die erwarteten Ergebnisse? Es gibt eine Erwähnung, dass es, um zu funktionieren, notwendig sein könnte, eine CPS-bewusste "foreach" zu haben, aber auf jeden Fall wäre es hilfreich zu wissen, was das Problem ist. –
Es kompiliert nicht. – Yang
Vielleicht möchten Sie die Antwort von Miles Sabin auf eine ähnliche Frage überprüfen, die ich http://stackoverflow.com/questions/2137619/scala-eqivalent-to-python-generators/2146456#2146456 hatte. Nicht sicher, dass es dich näher bringt. – huynhjl