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

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

问题描述

如何使用 Scala 延续实现 C# yield return?我希望能够以相同的风格编写 Scala Iterators.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 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.

推荐答案

在我们介绍延续之前,我们需要构建一些基础设施.下面是一个 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.

我们使用 shiftreset 操作符将计算分解为 Iteration,然后用trampolineIterations变成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# 参考页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屋!

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