2012年5月4日金曜日

Scheme第14歩

継続の話その2。

C言語等のスタックに慣れていると、ぎょっとするようなことができてしまう。
(define cont 0)
(let loop
  ((i 0) (sum 0))
  (if (> sum 1000)
    (begin (call/cc (lambda (c) (set! cont c))) (print "i=" i ", sum=" sum))
    (loop (+ i 1) (+ sum i))
  )
)
このコード。call/ccを除くと、0+1+2+3+...と1000を越えるまで足し込んでいるだけ。また、call/ccで実行されるプロシージャの中では、渡された継続cを変数contに取っておいてるだけで、継続自体の呼び出しはしていない。したがって、このコードを実行してもcall/cc部では特に何も起きずに、
i=47, sum=1035
と表示される。まあ、ここまではいいとして、問題はここから。
上のコードを実行すると、変数contにはprintする直前の継続が収められる。そこで
(cont)
と継続を呼び出すと、 何と再び
i=47, sum=1035
と表示されてしまう。

…物心ついたときからクロージャがあるような世代には当たり前かもしれないが、古い言語の場合、ある変数のスコープから抜けたとき、その変数のために割り当てていたメモリはさっさと明け渡される。例えばC言語でsetjmp/longjmpを使って同じようなことをやろうとしても、iやsumはループを抜けた時点で電子の藻屑になっている可能性がある。そのため、仮にlongjmpで戻っても同じ値が表示される保証が全くない。

1度スコープを抜けても変数が消滅しないからこそ、Schemeでは継続を使って縦横無尽に飛び回れるのだ。

0 件のコメント:

コメントを投稿