C言語等のスタックに慣れていると、ぎょっとするようなことができてしまう。
(define cont 0)このコード。call/ccを除くと、0+1+2+3+...と1000を越えるまで足し込んでいるだけ。また、call/ccで実行されるプロシージャの中では、渡された継続cを変数contに取っておいてるだけで、継続自体の呼び出しはしていない。したがって、このコードを実行してもcall/cc部では特に何も起きずに、
(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))
)
)
i=47, sum=1035と表示される。まあ、ここまではいいとして、問題はここから。
上のコードを実行すると、変数contにはprintする直前の継続が収められる。そこで
(cont)と継続を呼び出すと、 何と再び
i=47, sum=1035と表示されてしまう。
…物心ついたときからクロージャがあるような世代には当たり前かもしれないが、古い言語の場合、ある変数のスコープから抜けたとき、その変数のために割り当てていたメモリはさっさと明け渡される。例えばC言語でsetjmp/longjmpを使って同じようなことをやろうとしても、iやsumはループを抜けた時点で電子の藻屑になっている可能性がある。そのため、仮にlongjmpで戻っても同じ値が表示される保証が全くない。
1度スコープを抜けても変数が消滅しないからこそ、Schemeでは継続を使って縦横無尽に飛び回れるのだ。
0 件のコメント:
コメントを投稿