Scala entspricht Python-Generatoren?

Ist es möglich, in Scala etwas zu implementieren, was der Pythonyield , wo es sich an den lokalen Zustand der Funktion erinnert, wo es verwendet wird und "den nächsten Wert" jedesmal, wenn es aufgerufen wird "

Ich wollte so etwas haben, um eine rekursive Funktion in einen Iterator umzuwandeln. Ähnliches wie folgt:

 # this is python def foo(i): yield i if i > 0: for j in foo(i - 1): yield j for i in foo(5): print i 

Ausgenommen, foo kann komplexer sein und wiederholt sich durch einige azyklische Objekt-Grafik.

Zusätzliche Bearbeiten: Lass mich ein komplexeres Beispiel hinzufügen (aber immer noch einfach): Ich kann eine einfache rekursive Funktion schreiben, die Dinge wie es geht:

 // this is Scala def printClass(clazz:Class[_], indent:String=""): Unit = { clazz match { case null => case _ => println(indent + clazz) printClass(clazz.getSuperclass, indent + " ") for (c <- clazz.getInterfaces) { printClass(c, indent + " ") } } } 

Idealerweise möchte ich gern eine Bibliothek haben, die mir erlaubt, ein paar Aussagen leicht zu ändern und es als Iterator zu arbeiten:

 // this is not Scala def yieldClass(clazz:Class[_]): Iterator[Class[_]] = { clazz match { case null => case _ => sudoYield clazz for (c <- yieldClass(clazz.getSuperclass)) sudoYield c for (c <- clazz.getInterfaces; d <- yieldClasss(c)) sudoYield d } } 

Es scheint, dass Fortsetzungen das erlauben, das zu tun, aber ich verstehe einfach nicht das shift/reset . Wird die Fortsetzung schließlich in den Hauptcompiler verwandeln und wäre es möglich, die Komplexität in einer Bibliothek herauszuholen?

Bearbeiten 2: Überprüfen Sie Richs Antwort in diesem anderen Thread.

4 Solutions collect form web for “Scala entspricht Python-Generatoren?”

Während Python-Generatoren cool sind, versuchen, sie zu duplizieren ist wirklich nicht der beste Weg, um in Scala zu gehen. Zum Beispiel, der folgende Code macht die gleichwertige Arbeit, was Sie wollen:

 def classStream(clazz: Class[_]): Stream[Class[_]] = clazz match { case null => Stream.empty case _ => ( clazz #:: classStream(clazz.getSuperclass) #::: clazz.getInterfaces.toStream.flatMap(classStream) #::: Stream.empty ) } 

In ihm wird der Stream fasziniert, also wird es keines der Elemente verarbeiten, bis es gefragt wird, was du überprüfen kannst, indem du dies laufst:

 def classStream(clazz: Class[_]): Stream[Class[_]] = clazz match { case null => Stream.empty case _ => ( clazz #:: { println(clazz.toString+": super"); classStream(clazz.getSuperclass) } #::: { println(clazz.toString+": interfaces"); clazz.getInterfaces.toStream.flatMap(classStream) } #::: Stream.empty ) } 

Das Ergebnis kann in einen Iterator einfach umgewandelt werden, indem man .iterator auf dem resultierenden Stream .iterator :

 def classIterator(clazz: Class[_]): Iterator[Class[_]] = classStream(clazz).iterator 

Die foo Definition, mit Stream , würde also gerendert:

 scala> def foo(i: Int): Stream[Int] = i #:: (if (i > 0) foo(i - 1) else Stream.empty) foo: (i: Int)Stream[Int] scala> foo(5) foreach println 5 4 3 2 1 0 

Eine andere Alternative wäre die Verknüpfung der verschiedenen Iteratoren, wobei man darauf achtet, sie nicht vorzurechnen. Hier ist ein Beispiel, auch mit Debugging-Nachrichten zu helfen, die Ausführung zu verfolgen:

 def yieldClass(clazz: Class[_]): Iterator[Class[_]] = clazz match { case null => println("empty"); Iterator.empty case _ => def thisIterator = { println("self of "+clazz); Iterator(clazz) } def superIterator = { println("super of "+clazz); yieldClass(clazz.getSuperclass) } def interfacesIterator = { println("interfaces of "+clazz); clazz.getInterfaces.iterator flatMap yieldClass } thisIterator ++ superIterator ++ interfacesIterator } 

Dies ist ziemlich nah an Ihrem Code. Statt sudoYield ich Definitionen, und dann habe ich sie einfach verkettet, wie ich es wünsche.

Also, während dies eine Nicht-Antwort ist, denke ich nur, dass du hier den falschen Baum bellst. Der Versuch, Python in Scala zu schreiben, ist verpflichtet, unproduktiv zu sein. Arbeiten Sie härter an den Scala-Idiomen, die die gleichen Ziele erreichen.

Eine weitere Fortsetzung Plugin-basierte Lösung, diesmal mit einem mehr oder weniger gekapselten Generator-Typ,

 import scala.continuations._ import scala.continuations.ControlContext._ object Test { def loopWhile(cond: =>Boolean)(body: =>(Unit @suspendable)): Unit @suspendable = { if (cond) { body loopWhile(cond)(body) } else () } abstract class Generator[T] { var producerCont : (Unit => Unit) = null var consumerCont : (T => Unit) = null protected def body : Unit @suspendable reset { body } def generate(t : T) : Unit @suspendable = shift { (k : Unit => Unit) => { producerCont = k if (consumerCont != null) consumerCont(t) } } def next : T @suspendable = shift { (k : T => Unit) => { consumerCont = k if (producerCont != null) producerCont() } } } def main(args: Array[String]) { val g = new Generator[Int] { def body = { var i = 0 loopWhile(i < 10) { generate(i) i += 1 } } } reset { loopWhile(true) { println("Generated: "+g.next) } } } } 

Um dies in einer allgemeinen Weise zu tun, denke ich, dass Sie das Fortsetzungs-Plugin benötigen.

Eine naive Umsetzung (freihändig, nicht kompiliert / geprüft):

 def iterator = new { private[this] var done = false // Define your yielding state here // This generator yields: 3, 13, 0, 1, 3, 6, 26, 27 private[this] var state: Unit=>Int = reset { var x = 3 giveItUp(x) x += 10 giveItUp(x) x = 0 giveItUp(x) List(1,2,3).foreach { i => x += i; giveItUp(x) } x += 20 giveItUp(x) x += 1 done = true x } // Well, "yield" is a keyword, so how about giveItUp? private[this] def giveItUp(i: Int) = shift { k: (Unit=>Int) => state = k i } def hasNext = !done def next = state() } 

Was passiert ist, ist, dass jeder Aufruf zum shift den Kontrollfluss von dort abruft, wo er zum Ende des reset Blocks aufgerufen wird, den er aufgerufen hat. Dies wird als das k Argument in die Schaltfunktion übergeben.

Also, im obigen Beispiel gibt jeder giveItUp(x) den Wert von x (bis zu diesem Punkt) zurück und speichert den Rest der Berechnung in der Zustandsvariable. Es wird von außen durch die hasNext und die next Methoden angetrieben.

Gehen Sie sanft, das ist offensichtlich eine schreckliche Möglichkeit, dies zu implementieren. Aber am besten konnte ich spät in der Nacht ohne Compiler handlich machen.

Scala's For-Loop des Formulars for (e <- Producer) f(e) übersetzt in einen foreach Aufruf und nicht direkt in iterator / next .

Im foreach brauchen wir nicht, die Kreationen der Objekte zu linearisieren und sie an einem Ort zu haben, wie es für den next Iterator erforderlich wäre. Die Verbraucherfunktion f kann mehrfach eingefügt werden, genau dort, wo sie benötigt wird (dh wo ein Objekt angelegt wird).

Dies macht die Implementierung von Anwendungsfällen für Generatoren einfach und effizient mit Traversable / foreach in Scala.


Das erste Foo-Beispiel:

 case class Countdown(start: Int) extends Traversable[Int] { def foreach[U](f: Int => U) { var j = start while (j >= 0) {f(j); j -= 1} } } for (i <- Countdown(5)) println(i) // or equivalent: Countdown(5) foreach println 

Das erste printClass-Beispiel:

  // v1 (without indentation) case class ClassStructure(c: Class[_]) { def foreach[U](f: Class[_] => U) { if (c eq null) return f(c) ClassStructure(c.getSuperclass) foreach f c.getInterfaces foreach (ClassStructure(_) foreach f) } } for (c <- ClassStructure(<foo/>.getClass)) println(c) // or equivalent: ClassStructure(<foo/>.getClass) foreach println 

Oder mit Einrückung:

  // v2 (with indentation) case class ClassWithIndent(c: Class[_], indent: String = "") { override def toString = indent + c } implicit def Class2WithIndent(c: Class[_]) = ClassWithIndent(c) case class ClassStructure(cwi: ClassWithIndent) { def foreach[U](f: ClassWithIndent => U) { if (cwi.c eq null) return f(cwi) ClassStructure(ClassWithIndent(cwi.c.getSuperclass, cwi.indent + " ")) foreach f cwi.c.getInterfaces foreach (i => ClassStructure(ClassWithIndent(i, cwi.indent + " ")) foreach f) } } for (c <- ClassStructure(<foo/>.getClass)) println(c) // or equivalent: ClassStructure(<foo/>.getClass) foreach println 

Ausgabe:

 class scala.xml.Elem class scala.xml.Node class scala.xml.NodeSeq class java.lang.Object interface scala.collection.immutable.Seq interface scala.collection.immutable.Iterable interface scala.collection.immutable.Traversable interface scala.collection.Traversable interface scala.collection.TraversableLike interface scala.collection.generic.HasNewBuilder interface scala.collection.generic.FilterMonadic interface scala.collection.TraversableOnce interface scala.ScalaObject interface scala.ScalaObject interface scala.collection.generic.GenericTraversableTemplate interface scala.collection.generic.HasNewBuilder interface scala.ScalaObject interface scala.ScalaObject interface scala.collection.generic.GenericTraversableTemplate interface scala.collection.generic.HasNewBuilder interface scala.ScalaObject interface scala.collection.TraversableLike interface scala.collection.generic.HasNewBuilder interface scala.collection.generic.FilterMonadic interface scala.collection.TraversableOnce interface scala.ScalaObject interface scala.ScalaObject interface scala.Immutable interface scala.ScalaObject interface scala.collection.Iterable interface scala.collection.Traversable interface scala.collection.TraversableLike interface scala.collection.generic.HasNewBuilder interface scala.collection.generic.FilterMonadic interface scala.collection.TraversableOnce interface scala.ScalaObject interface scala.ScalaObject interface scala.collection.generic.GenericTraversableTemplate interface scala.collection.generic.HasNewBuilder interface scala.ScalaObject interface scala.ScalaObject interface scala.collection.generic.GenericTraversableTemplate interface scala.collection.generic.HasNewBuilder interface scala.ScalaObject interface scala.collection.IterableLike interface scala.Equals interface scala.collection.TraversableLike interface scala.collection.generic.HasNewBuilder interface scala.collection.generic.FilterMonadic interface scala.collection.TraversableOnce interface scala.ScalaObject interface scala.ScalaObject interface scala.ScalaObject interface scala.ScalaObject interface scala.collection.generic.GenericTraversableTemplate interface scala.collection.generic.HasNewBuilder interface scala.ScalaObject interface scala.collection.IterableLike interface scala.Equals interface scala.collection.TraversableLike interface scala.collection.generic.HasNewBuilder interface scala.collection.generic.FilterMonadic interface scala.collection.TraversableOnce interface scala.ScalaObject interface scala.ScalaObject interface scala.ScalaObject interface scala.ScalaObject interface scala.collection.Seq interface scala.PartialFunction interface scala.Function1 interface scala.ScalaObject interface scala.ScalaObject interface scala.collection.Iterable interface scala.collection.Traversable interface scala.collection.TraversableLike interface scala.collection.generic.HasNewBuilder interface scala.collection.generic.FilterMonadic interface scala.collection.TraversableOnce interface scala.ScalaObject interface scala.ScalaObject interface scala.collection.generic.GenericTraversableTemplate interface scala.collection.generic.HasNewBuilder interface scala.ScalaObject interface scala.ScalaObject interface scala.collection.generic.GenericTraversableTemplate interface scala.collection.generic.HasNewBuilder interface scala.ScalaObject interface scala.collection.IterableLike interface scala.Equals interface scala.collection.TraversableLike interface scala.collection.generic.HasNewBuilder interface scala.collection.generic.FilterMonadic interface scala.collection.TraversableOnce interface scala.ScalaObject interface scala.ScalaObject interface scala.ScalaObject interface scala.ScalaObject interface scala.collection.generic.GenericTraversableTemplate interface scala.collection.generic.HasNewBuilder interface scala.ScalaObject interface scala.collection.SeqLike interface scala.collection.IterableLike interface scala.Equals interface scala.collection.TraversableLike interface scala.collection.generic.HasNewBuilder interface scala.collection.generic.FilterMonadic interface scala.collection.TraversableOnce interface scala.ScalaObject interface scala.ScalaObject interface scala.ScalaObject interface scala.ScalaObject interface scala.ScalaObject interface scala.collection.generic.GenericTraversableTemplate interface scala.collection.generic.HasNewBuilder interface scala.ScalaObject interface scala.collection.SeqLike interface scala.collection.IterableLike interface scala.Equals interface scala.collection.TraversableLike interface scala.collection.generic.HasNewBuilder interface scala.collection.generic.FilterMonadic interface scala.collection.TraversableOnce interface scala.ScalaObject interface scala.ScalaObject interface scala.ScalaObject interface scala.ScalaObject interface scala.ScalaObject interface scala.collection.SeqLike interface scala.collection.IterableLike interface scala.Equals interface scala.collection.TraversableLike interface scala.collection.generic.HasNewBuilder interface scala.collection.generic.FilterMonadic interface scala.collection.TraversableOnce interface scala.ScalaObject interface scala.ScalaObject interface scala.ScalaObject interface scala.ScalaObject interface scala.xml.Equality interface scala.Equals interface scala.ScalaObject interface scala.ScalaObject interface scala.ScalaObject interface scala.ScalaObject interface java.io.Serializable 
  • Welche Funktion im Funken wird verwendet, um zwei RDDs mit den Tasten zu kombinieren
  • Was ist der bevorzugte Weg, um 'Ertrag' in Scala zu implementieren?
  • Listing alle Dateien in Spark Cluster auf Hadoop HDFS mit Scala oder Python gespeichert?
  • Zebra-Puzzle in der Scala
  • Slice Notation in Scala?
  • Was sind die Spark-Transformationen, die einen Shuffle verursachen?
  • Interpretation einer Benchmark in C, Clojure, Python, Ruby, Scala und andere
  • Funkenleistung für Scala vs Python
  • Spark: Wie komme ich Python mit Scala oder Java User Defined Functions?
  • Wenn du Python als Unterprozeß anrufst, kann ich es zwingen, im interaktiven Modus zu laufen?
  • Mehrfach-Funken-Bewerbungsunterlagen im Standalone-Modus
  • Python ist die beste Programmiersprache der Welt.