scala scala-2.8 yield continuations yield-return

Implementando rendimiento(retorno de rendimiento) usando continuaciones de Scala



scala-2.8 yield (2)

Antes de presentar las continuaciones, necesitamos construir cierta infraestructura. Debajo hay un trampoline que opera en objetos de Iteration . Una iteración es un cálculo que puede Yield un nuevo valor o puede hacerse.

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 }

El trampolín utiliza un bucle interno que convierte la secuencia de objetos de Iteration en una Stream . Luego obtenemos un Iterator al invocar el iterator en el objeto de flujo resultante. Al usar un Stream nuestra evaluación es floja; no evaluamos nuestra próxima iteración hasta que se necesite.

El trampolín se puede usar para construir un iterador directamente.

val itr1 = trampoline { Yield(1, () => Yield(2, () => Yield(3, () => Done))) } for (i <- itr1) { println(i) }

Eso es bastante horrible de escribir, así que usemos continuaciones delimitadas para crear nuestros objetos de Iteration automáticamente.

Usamos los operadores de shift y reset para dividir el cálculo en Iteration , luego usamos trampoline para convertir la Iteration en un Iterator .

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

Ahora podemos reescribir nuestro ejemplo.

val itr2 = iterator[Int] { yld(1) yld(2) yld(3) } for (i <- itr2) { println(i) }

¡Mucho mejor!

Ahora aquí hay un ejemplo de la página de referencia de C # para yield que muestra un uso más avanzado. Puede ser un poco complicado acostumbrarse a los tipos, pero todo funciona.

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

¿Cómo se puede implementar el yield return C # utilizando las continuaciones de Scala? Me gustaría poder escribir Scala Iterator en el mismo estilo. Una puñalada está en los comentarios en esta publicación de noticias de Scala , pero no funciona (intenté usar la versión beta de Scala 2.8.0). Las respuestas en una pregunta relacionada sugieren que esto es posible, pero aunque he estado jugando con continuaciones delimitadas por un tiempo, parece que no puedo entender exactamente cómo hacerlo.


Logré descubrir una manera de hacerlo, después de unas horas más de jugar. Pensé que era más fácil de entender que todas las otras soluciones que he visto hasta ahora, aunque luego valoré mucho Miles'' soluciones de Rich y Miles'' .

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)