Scala で Python の Generator みたいな何か(継続版)

継続を使えばこの前作った Generator のような何かを概念通りに書けそうだったので書いてみた。

ソース

ソースは gist

概要は大体前回の Actor を使った版と同じ。

使い方

import scala.util.continuations.{cpsParam}
import net.shomah4a.utils.generator.continuation.Generator.{gen, yld, GenParam}


def main()
{

  for (v <- gen[Int] {
    yld(1)
    yld(1)
    yld(2)
    yld(3)
  })
    {
      println(v)
    }
}

main()

gen 関数に引数なしの関数を渡し、その中で yld(x) した値を順にイテレートする。
この場合の結果は

1
1
2
3

となる。

処理の順番は
yld(1) -> println(v) -> yld(1) -> println(v) -> yld(2) -> println(v) -> yld(3) -> println(v)
で、 yld を呼んだ段階でその値が v に入り、 for のブロックが実行される。
継続を使っているので処理はすべて同じスレッドで行われる。所謂協調スレッド。

サンプル

意味のあるサンプルとしてフィボナッチ数を逐次計算する処理を書いてみた。

import net.shomah4a.utils.generator.continuation.Generator.{gen, yld, GenState}

def main()
{
  def fib(x: Int, y:Int) : Unit @GenState[Int] = 
  {
    yld(x+y)
    fib(y, x+y)
  }

  for (v <- gen[Int] {
    yld(1)
    yld(1)

    fib(1,1)
  })
    {
      println(v) // フィボナッチ数を受け取って出力
      Thread.sleep(100) // 速すぎて見えないので sleep しておく
    }
}

main()

継続のなかで使う関数は @GenState を付けないといけないので、一関数で書ける範囲に抑えておく方がいいのかもしれない。
あとは while などで逐次処理するとか。

それ以外は前回より自然に書けているような気がする。

実行結果

オーバフローしてしまうのでイマイチだが、フィボナッチ数を逐次計算できているのがわかる。
C-c なんかで止めないと止まらないので注意。

1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765
10946
17711
28657
46368
75025
121393
196418
317811
514229
832040
1346269
2178309
3524578
5702887
9227465
14930352
24157817
39088169
63245986
102334155
165580141
267914296
433494437
701408733
1134903170
1836311903
-1323752223
512559680
-811192543
-298632863
-1109825406
-1408458269
1776683621
368225352
...