在引入延续之前,我们需要构建一些基础设施。
下面是一个蹦床 http://blog.richdougherty.com/2009/04/tail-calls-tailrec-and-trampolines.html运行于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
}
蹦床使用一个内部循环来转动序列Iteration
物体变成Stream
。
然后我们得到一个Iterator
通过致电iterator
在生成的流对象上。
通过使用Stream
我们的评价是懒惰的;在需要之前我们不会评估下一次迭代。
蹦床可以用来直接构建迭代器。
val itr1 = trampoline {
Yield(1, () => Yield(2, () => Yield(3, () => Done)))
}
for (i <- itr1) { println(i) }
这写起来非常可怕,所以让我们使用分隔延续来创建我们的Iteration
自动对象。
我们使用shift
and reset
运算符将计算分解为Iteration
s,
然后使用trampoline
转动Iteration
变成一个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(())))
现在我们可以重写我们的示例。
val itr2 = iterator[Int] {
yld(1)
yld(2)
yld(3)
}
for (i <- itr2) { println(i) }
好多了!
现在这是一个来自C# 参考页 http://msdn.microsoft.com/en-us/library/9k7k7cf0.aspx for 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) }