使用 Scala 延续实现收益(收益回报) [英] Implementing yield (yield return) using Scala continuations
问题描述
如何使用 Scala 延续实现 C# yield return
?我希望能够以相同的风格编写 Scala Iterator
s.this Scala news post 的评论中有刺伤,但它不起作用(尝试使用 Scala 2.8.0 测试版).相关问题中的答案表明这是可能的,但尽管我一直在玩带分隔符的延续一段时间,我似乎无法完全理解如何做到这一点.
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.
推荐答案
在我们介绍延续之前,我们需要构建一些基础设施.下面是一个 trampoline 操作 迭代
对象.迭代是一种计算,它可以产生
一个新值,也可以完成
.
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
}
trampoline 使用内部循环将 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.
trampoline 可以直接用来构建迭代器.
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
s变成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# 参考页中yield
显示了一些更高级的用法.这些类型可能有点难以习惯,但一切正常.
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屋!