継続

正しいことと分かりやすい部分は、Gemmaさんのおかげ。間違ってるのと分かりづらい所は俺のせい。

継続でできること

まずは、継続を使うとできることから。

大域脱出

一番簡単なのが大域脱出。

(call/cc (lambda(return)
  	  (display "before exit\n")
	  (return 0)
	  (display "never execute this line\n")))

実行結果:

before exit

これは、try/catchと同じ事を継続で実現している。
つまり、継続さえあれば、try/catchは不要になる。

非決定性問題

継続を使うと、こんなステキなamb関数が書ける。

; iは[1,2,3]のうちどれか
(define i (amb 1 2 3))

;; jは[2,5,8]のうちのどれか
(define j (amb 3 5 8))

(if (prime? (+ i j))
 (cons i j)  ; i+jが素数なら、その対を返す
 (amb))      ; そうでないなら、失敗として扱う。

これで、(2,3)という対を得ることができる。

このamb関数の別名が「天使の演算子」らしい。

継続とは

簡単に言えば、スタックの保存。

スタックマシン

まずは、(+ 1 (* 2 3))という式を考えよう。

これがスタックマシンで実行されるとすると、次のように実行される。

"+"のpush

+

"1"のpush

1
+

"*"のpush

*
1
+

"2"のpush

2
*
1
+

"3"のpush

3
2
*
1
+

")"に達したので先頭2つをpopしてかけ算を計算する。
そして、結果をpushする。

6
1
+

")"に達したので、先頭2つをpopして足し算を計算する。
そして、結果をpushする。

7

本当は、スタックマシンなら逆ポーランド記法を使うのが分かりやすい。
でも、今後の説明の関係上、これで我慢してくださいな。

継続

次に、

(define s #f)
(display (+ 1 (call/cc (lambda(cc)
	 	      	(set! s cc)
			2))))
(display #\Newline)

(display (s 5))
(display #\Newline)
(display (s 6))
(display #\Newline)

について考える。

まず、これを実行すると次のようになる。

3
6
7

(+ 1 (callcc (lambda(cc) ....)))について考える。
(+ 1 ...)まで実行した段階で、スタックは次のような状態になる。

1
+

そして、ccにはこのスタックが保存される。
より正確に言うと『ccを呼ぶと、このスタックの状態が復元されて、処理が継続される』。

つまり、ccは次の関数と等価になる。

(define (cc x) (+ 1 x))

# だめだ。あんまり分かりやすくない。Gemmaさんの説明は分かりやすかったのに・・。