首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用Scala延续实现yield (yield )

使用Scala延续实现yield (yield )
EN

Stack Overflow用户
提问于 2010-02-05 01:45:45
回答 2查看 7.1K关注 0票数 27

如何使用Scala continuations实现C# yield return?我希望能够以相同的风格编写Scala Iterator。在this Scala news post上的评论中有刺痛,但它不起作用(尝试使用Scala 2.8.0测试版)。related question中的答案表明这是可能的,但尽管我已经尝试了一段时间,但我似乎不能完全理解如何做到这一点。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2010-02-07 07:34:53

在引入延续之前,我们需要构建一些基础设施。下面是一个对Iteration对象进行操作的trampoline。迭代是一种计算,它既可以Yield一个新值,也可以是Done

代码语言:javascript
复制
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,我们的评估是惰性的;我们不会评估我们的下一次迭代,直到需要它。

trampoline可以用来直接构建迭代器。

代码语言:javascript
复制
val itr1 = trampoline {
  Yield(1, () => Yield(2, () => Yield(3, () => Done)))
}

for (i <- itr1) { println(i) }

这很难写,所以让我们使用带分隔符的continuations来自动创建Iteration对象。

我们使用shiftreset运算符将计算分解为Iterations,然后使用trampolineIterations转换为Iterator

代码语言:javascript
复制
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(())))

现在我们可以重写我们的例子了。

代码语言:javascript
复制
val itr2 = iterator[Int] {
  yld(1)
  yld(2)
  yld(3)
}

for (i <- itr2) { println(i) }

好多了!

现在,这里有一个来自yieldC# reference page的例子,它展示了一些更高级的用法。要习惯这些类型可能有点棘手,但它们都可以正常工作。

代码语言:javascript
复制
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) }
票数 41
EN

Stack Overflow用户

发布于 2010-02-08 06:11:52

在玩了几个小时之后,我设法找到了一种方法来做到这一点。我认为这比到目前为止我所见过的所有其他解决方案更容易理解,尽管后来我确实非常欣赏Rich的和Miles'的解决方案。

代码语言:javascript
复制
def loopWhile(cond: =>Boolean)(body: =>(Unit @suspendable)): Unit @suspendable = {
  if (cond) {
    body
    loopWhile(cond)(body)
  }
}

  class Gen {
    var prodCont: Unit => Unit = { x: Unit => prod }
    var nextVal = 0
    def yld(i: Int) = shift { k: (Unit => Unit) => nextVal = i; prodCont = k }
    def next = { prodCont(); nextVal }
    def prod = {
      reset {
        // following is generator logic; can be refactored out generically
        var i = 0
        i += 1
        yld(i)
        i += 1
        yld(i)
        // scala continuations plugin can't handle while loops, so need own construct
        loopWhile (true) {
          i += 1
          yld(i)
        }
      }
    }
  }
  val it = new Gen
  println(it.next)
  println(it.next)
  println(it.next)
票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/2201882

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档