続・継続

昨日の時点ではまだバグがあったが、そのバグも直った。たぶんこれで完成したと思う。最終的にはスタックですらなく、単に現在の継続を覚えておくだけで良くなった。これは継続自身に次に処理を渡すべき継続の参照を持たせることにしたためで、こうしないと継続を再利用した時に問題が出る。

昨日は結果しか書かなかったので、継続について少し解説を。

> (+ (call/cc (lambda (x) (x 1) 2)) 3)
==> 4

この例では、xにcall/ccの外側への継続が束縛されているので、xを呼ぶと(call/cc ...)全体がその引数と置き換わり、全体としては(+ 1 3)を計算することになる。いわゆる大域脱出の例。

別の例:

(define cont #f)
==> #f

(+ (call/cc (lambda (x) (set! cont x) 1)) 2)
==> 3

(cont 5)
==> 7

今度は、call/ccの中で渡された継続は使われていないが、その代わり変数に保存している。この保存された継続を呼び出すと、途中の計算からやり直すことができる。つまり、(cont 5)は前の式の(call/cc ...)の部分に5が入って(+ 5 2)を計算していることになる。

まとめると、継続を使うと中→外、外→中の大域移動ができる(それに対してC言語のlongjmpやJava/C++の例外は中から外にしか移動できない)。ちょうど、call/cc全体とその中で渡された変数がワームホールのようにつながっていて、しかもその変数を外に引きずり出してくることができる、とイメージすると分かりやすいと思う。

以下は昔書いた文章へのリンク。

ダウンロード

需要があるかどうかは分からないけど、とりあえず公開してみる。

バージョン1とバージョン2の違いは継続があるかないか。どちらもレキシカルスコープで、変数と関数の名前空間は同一、関数の名前はScheme寄り。実はこれより前に、ダイナミックスコープで変数と関数の名前空間が別というもっとEmacs Lisp寄りなバージョン0があったんだけど、もう残ってない。ただその名残でdefmacroなんていうEmacs Lispっぽい名前も残ってたりする。

あらかじめ断っておくと、決して実用向きではないのでそのつもりで。ドキュメントもない単なる現状のスナップショットです。ソースコードを研究してやろうという人だけどうぞ。