que iteradores generadores generador español codigo python scala

iteradores - ¿Scala equivalente a los generadores Python?



que es un iterable en python (4)

¿Es posible implementar en Scala algo equivalente a la declaración de yield Python, donde recuerda el estado local de la función donde se usa y "cede" el siguiente valor cada vez que se llama?

Quería tener algo como esto para convertir una función recursiva en un iterador. Más o menos así:

# 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

Excepto, foo puede ser más complejo y recurrir a través de un gráfico de objetos acíclicos.

Edición adicional: permítame agregar un ejemplo más complejo (pero aún simple): puedo escribir una función recursiva simple imprimiendo cosas a medida que avanza:

// 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 + " ") } } }

Idealmente, me gustaría tener una biblioteca que me permita cambiar fácilmente algunas declaraciones y hacer que funcione como un iterador:

// 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 } }

Parece que las continuaciones permiten hacer eso, pero simplemente no entiendo el concepto de shift/reset . ¿La continuación eventualmente se convertirá en el compilador principal y sería posible extraer la complejidad en una biblioteca?

Edit 2: verifica la respuesta de Rich en ese otro hilo.


El for-loop de Scala de la forma for (e <- Producer) f(e) traduce en una llamada foreach , y no directamente en iterator / next .

En el foreach no es necesario que linealice las creaciones de los objetos y los tenga en un solo lugar, ya que sería necesario para el next iterador. La función del consumidor f se puede insertar varias veces, exactamente donde se necesita (es decir, donde se crea un objeto).

Esto hace que la implementación de casos de uso para generadores sea simple y eficiente con Traversable / foreach en Scala.

El primer ejemplo Foo:

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

El printClass-ejemplo inicial:

// 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

O con sangría:

// 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

Salida:

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


Otra solución basada en complementos de continuación, esta vez con un tipo de Generador más o menos encapsulado,

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) } } } }


Para hacer esto de una manera general, creo que necesita el complemento de continuación .

Una implementación ingenua (a mano alzada, no compilada / comprobada):

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() }

Lo que sucede es que cualquier llamada a shift captura el flujo de control desde donde se llama hasta el final del bloque de reset que se llama. Esto se pasa como el argumento k a la función shift.

Entonces, en el ejemplo anterior, cada giveItUp(x) devuelve el valor de x (hasta ese punto) y guarda el resto del cálculo en la variable de state . Es impulsado desde afuera por los métodos hasNext y next .

Vaya gentil, esta es obviamente una forma terrible de implementar esto. Pero lo mejor que podía hacer a altas horas de la noche sin un compilador a mano.


Si bien los generadores Python son geniales, intentar duplicarlos realmente no es la mejor manera de hacerlo en Scala. Por ejemplo, el siguiente código hace el trabajo equivalente a lo que desea:

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

En él, la secuencia se genera de forma diferida, por lo que no procesará ninguno de los elementos hasta que se solicite, lo que puede verificar ejecutando esto:

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 ) }

El resultado se puede convertir en un Iterator simplemente llamando .iterator en el Stream resultante:

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

La definición de foo , utilizando Stream , se representaría así:

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

Otra alternativa sería concatenar los distintos iteradores, teniendo cuidado de no precomputarlos. Aquí hay un ejemplo, también con los mensajes de depuración para ayudar a rastrear la ejecución:

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 }

Esto está muy cerca de tu código. En lugar de sudoYield , tengo definiciones, y luego las concatento como me gustaría.

Entonces, aunque esto no responde, creo que estás ladrando el árbol equivocado aquí. Tratar de escribir Python en Scala seguramente será improductivo. Trabaja más duro en las expresiones idiomáticas de Scala que logran los mismos objetivos.