Skip to content

Instantly share code, notes, and snippets.

@kmizu
Created July 24, 2011 13:07
Show Gist options
  • Save kmizu/1102602 to your computer and use it in GitHub Desktop.
Save kmizu/1102602 to your computer and use it in GitHub Desktop.
An example of generator library. This is used for explaining shift/reset and this code cannot be compiled Because @susp is eliminated.
import scala.continuations.ControlContext._
import scala.continuations._
import scala.collection._
/*
* C#形式のイテレータを表現したトレイト
*/
trait Generator[+A] extends Traversable[A] {
def moveNext(): Boolean
def current: A
//ここではこれだけ実装しておけば、後の高階関数は勝手に定義してくれる
def foreach[U](f: A => U): Unit = {
while(moveNext()) {
f(current)
}
}
}
object Generator {
def make[A](body: (A => Unit) => Unit): Generator[A] = new Generator[A] {
val yields: A => Unit = {v => // vはyieldの呼び出し時に渡される値
//kの中には、「yieldsが呼ばれた後の実行状態」が保存されていて、kを呼び出すと、ユーザ側コードの実行が再開される
shift{k: (Unit => Any) =>
//kとvの組を返す
(k, v):Any //(X) shiftの呼び出しが終わると、再内のresetの呼出し後(1)まで大域的に脱出してしまう。
}
}
//(A)thunkは実行状態を保存しておくための変数。初期状態では、↓のようになっているが、それ以降は継続が代入される。
var thunk: Unit => Any = {x: Unit =>
//実行状態をキャプチャする準備
reset {
//引数にyields↑を渡してbodyを呼び出す
body(yields) //この行の実行中に、yieldが呼ばれたら、一気に(1)まで脱出する((X)に対応)。
None //yieldsが呼び出せるブロックの実行が全て終わったらここに到達する。このとき、resetの返り値はNoneになる(=終了)
}
//(1) (k, v)の組がresetの戻り値になる。
}
var value: A = _
def moveNext(): Boolean = {
val result = thunk() //thunkの呼び出し。初回は、上記の(A)に代入された関数が呼び出される。
//結果をパターンマッチ。(k, v)の組かNoneのどちらかであるはず。
result match {
//継続と値のペアが返って来た、ということはまだ「次」があるということ
case (k, v) =>
//型がAnyになってしまっているので、キャストして代入
thunk = k.asInstanceOf[(Unit => Any)] //thunkにキャプチャした実行状態が保存される
value = v.asInstanceOf[A] //yieldで渡された値をvalueに代入
true //次があるのでtrue
case None =>
false //ブロックの最後まで到達したのでfalse
}
}
def current: A = value
}
}
import Generator._
object User {
def upto[A](from: Int, to: Int)(f: Int => A ): Unit = {
if(from <= to) { f(from); upto(from + 1, to)(f) }
}
def main(args: Array[String]) {
// 1から10までの数を列挙するジェネレータ
val g1 = make[Int] {yields =>
upto(1, 10) {i =>
yields(i) これを呼び出すと、Generator.scala中の`yields`が呼ばれる
}
}
while(g1.moveNext()) {
println(g1.current)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment