Scala で Python の Generator みたいな何か(継続版)
継続を使えばこの前作った Generator のような何かを概念通りに書けそうだったので書いてみた。
使い方
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 ...