継続

ここら辺のコードに関する話を勤務先で聞いた。追記予定。

(追記: 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 に格納される。こうしてループが作成される。

やっぱり文章だと難しいね。

(/追記)