使用Scala延续实现收益(收益回报) [英] Implementing yield (yield return) using Scala continuations

查看:118
本文介绍了使用Scala延续实现收益(收益回报)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何使用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 Iterators 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.

我们使用shiftreset运算符将计算分解为Iteration, 然后使用trampolineIteration转换为Iterator.

We use the shift and reset operators to break the computation up into Iterations, then use trampoline to turn the Iterations 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆