使用Scala continuation实现yield(yield return)

Yan*_*ang 27 continuations scala yield yield-return scala-2.8

如何yield return使用Scala continuation 实现C#?我希望能够以Iterator相同的风格编写Scala .这篇Scala新闻帖的评论中有一个刺,但它不起作用(尝试使用Scala 2.8.0测试版).一个相关问题的答案表明这是可能的,但是虽然我已经玩了一段时间的分隔延续,但我似乎无法完全理解如何做到这一点.

Ric*_*rty 41

在我们介绍continuation之前,我们需要构建一些基础结构.下面是一个操作物体的蹦床Iteration.迭代是一种可以是Yield新值或者可以是新值的计算Done.

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
}
Run Code Online (Sandbox Code Playgroud)

蹦床使用内部循环将Iteration对象序列转换为a Stream.然后我们Iterator通过调用iterator生成的流对象来获取.通过使用Stream我们的评价是懒惰的; 在需要之前,我们不会评估下一次迭代.

蹦床可以直接用于构建迭代器.

val itr1 = trampoline {
  Yield(1, () => Yield(2, () => Yield(3, () => Done)))
}

for (i <- itr1) { println(i) }
Run Code Online (Sandbox Code Playgroud)

写这个非常糟糕,所以让我们使用分隔的延续来Iteration自动创建我们的对象.

我们使用shiftreset运算符将计算分解为Iterations,然后使用trampolineIterations转换为s 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(())))
Run Code Online (Sandbox Code Playgroud)

现在我们可以重写我们的例子.

val itr2 = iterator[Int] {
  yld(1)
  yld(2)
  yld(3)
}

for (i <- itr2) { println(i) }
Run Code Online (Sandbox Code Playgroud)

好多了!

现在这里是C#参考页面的一个例子,yield它显示了一些更高级的用法.这些类型可能有点难以习惯,但一切正常.

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) }
Run Code Online (Sandbox Code Playgroud)


Yan*_*ang 5

经过几个小时的游戏后,我设法找到了一种方法.我认为这比我到目前为止看到的所有其他解决方案更简单,尽管我之后非常了解Rich和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)
Run Code Online (Sandbox Code Playgroud)

  • 不再是.从while循环中调用cps代码已经有一段时间了.虽然仍然没有支持理解(我不认为他们会获得支持) (3认同)
  • Scala continuation无法处理while循环?哎哟! (2认同)