使用Scala延续实现收益(收益回报) [英] Implementing yield (yield return) using Scala continuations
问题描述
如何使用Scala延续实现C#yield return
?我希望能够以相同的样式编写Scala Iterator
. 此Scala新闻的评论中有一个刺痕,但它不起作用(使用Scala 2.8.0 beta进行了尝试).在相关问题中的答案表明这是可能的,但是尽管我一直在使用定界延续玩了一段时间,我似乎无法完全理解如何做到这一点.
How might one implement C# yield return
using Scala continuations? I'd like to be able to write Scala Iterator
s in the same style. A stab is in the comments on this Scala news post, but it doesn't work (tried using the Scala 2.8.0 beta). Answers in a related question suggest this is possible, but although I've been playing with delimited continuations for a while, I can't seem to exactly wrap my head around how to do this.
推荐答案
在介绍延续之前,我们需要构建一些基础结构.
以下是在Yield
新值或可以是Done
的计算.
Before we introduce continuations we need to build some infrastructure.
Below is a trampoline that operates on Iteration
objects.
An iteration is a computation that can either Yield
a new value or it can be 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
,我们的评估是懒惰的.在需要之前,我们不会评估下一次迭代.
The trampoline uses an internal loop that turns the sequence of Iteration
objects into a Stream
.
We then get an Iterator
by calling iterator
on the resulting stream object.
By using a Stream
our evaluation is lazy; we don't evaluate our next iteration until it is needed.
蹦床可用于直接构建迭代器.
The trampoline can be used to build an iterator directly.
val itr1 = trampoline {
Yield(1, () => Yield(2, () => Yield(3, () => Done)))
}
for (i <- itr1) { println(i) }
这太可怕了,所以让我们使用定界的延续来自动创建我们的Iteration
对象.
That's pretty horrible to write, so let's use delimited continuations to create our Iteration
objects automatically.
我们使用shift
和reset
运算符将计算分解为Iteration
,
然后使用trampoline
将Iteration
转换为Iterator
.
We use the shift
and reset
operators to break the computation up into Iteration
s,
then use trampoline
to turn the Iteration
s into an 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(())))
现在我们可以重写我们的示例.
Now we can rewrite our example.
val itr2 = iterator[Int] {
yld(1)
yld(2)
yld(3)
}
for (i <- itr2) { println(i) }
好多了!
现在,这是 C#参考页中针对
Now here's an example from the C# reference page for yield
that shows some more advanced usage.
The types can be a bit tricky to get used to, but it all works.
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) }
这篇关于使用Scala延续实现收益(收益回报)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!