継続
ここら辺のコードに関する話を勤務先で聞いた。追記予定。
(追記: 2012/04/07 14:40 PDT)
このときに聞いた話は:
- 継続はexecのようなもの
- system + exitのようなもの
- 実行したら終了するもの
- 継続でtry~catchに該当する制御構造を実現できる
@ujmに書いてもらったSchemeのコードにコメントを入れてみる。
(define (mein)
(print "mein")
(print (call/cc (lambda (c) (f 2 c)))))
(define (f x cont)
(print "f")
(g x
(lambda (y) (cont (+ y 1)))
cont))
(define (g x cont return)
(print "g")
(if (= x 2)
(return "oh..."))
(cont x))
(mein)
これの実行結果は:
mein
f
g
oh...
…になる。
call/ccは継続オブジェクトを生成し、call/ccの引数として与えられた関数に渡す。つまり:
(call/cc (lambda (c) (f 2 c)))
…このコードでは c は継続オブジェクトである。
これを評価すると f, g が順にコールされる。 g の中で:
(return "oh...")
…return は継続オブジェクトだから、これを評価しても処理は g のコール元である f には戻らず、call/ccを評価した箇所に戻る。つまり:
(print (call/cc (lambda (c) (f 2 c)))))
…では “oh…" が出力される。継続オブジェクトに与えられた引数が “oh…" だから。
ところで、これを文章で聞いて理解できる人はいるのだろうか…。
後でRubyのコードについても追記するかも。
(/追記)
(追記 2012/04/07 15:48 PDT)
今度は@ujmに書いてもらったRubyのコードにコメントを入れてみる。ぼくはRubyの練度が高くないけれど、callccの他は知識がなくても読めそう。
元々のコードが:
i = 0
c = callcc {|c| c}
p i
if i < 10
i += 1
c.call(c)
end
p :ok
…そして、これの実行結果が:
0
1
2
3
4
5
6
7
8
9
10
:ok
…ループができている。
c = callcc {|c| c}
…callccのブロック引数として現在の継続 c が渡されて、それがそのまま返る。すなわち、継続オブジェクトが作られ、それが変数 c に格納される。
次にifの中にある:
c.call(c)
…を実行すると callcc {|c| c}
を実行した時点に戻る。また、これの返り値は c なので再度継続オブジェクトが変数 c に格納される。こうしてループが作成される。
やっぱり文章だと難しいね。
(/追記)