call/cc リハビリ

ええと Dybvig 先生の dynamic-wind の実装なサンプルに call/cc が出てくる。後天性記憶不全にて、call/cc が全然イメージできてない状態になってます。仕方が無いので一年前にココ (使いたい人のための継続入門) で勉強しているログを見たんですが、ログの取り方が全然ダメ。後で確認してみ意味不明なのでは記録の意味ナシ。
とは言え、よく分からなかったのは違う方面で脳が一杯だったのかな、という事は記録がダメって訳ではないのかな、と言いつつ復習。

継続渡し

継続渡しの階乗の定義の例を以下に引用

;; 継続渡し形式の階乗の定義
(define (fact/cps n cont)
  (if (= n 0)
      (cont 1)
      (fact/cps (- n 1) (lambda (a) (cont (* n a))))))

_階乗の普通の定義_と違って、引数を一つとる手続きが渡される。これが_継続_です。継続って何か、というと Dybvig 先生のテキストによれば、_どのような式でもそれを評価している各時点において、その完了、あるいは少なくともその計算途中からの継続 (continuation) と呼ぶのです。_とある。
例えば、単純に計算結果を戻すのみ、であれば以下なソレで OK との事。

gosh> (fact/cps 5 identity)

上記の式の意図としては、5 の階乗の計算結果を identity に渡す、という事で、5 の階乗を計算する処理の継続が identity 手続き、という事かと。
例示されているナニで言えば

(* 2 (fact 10))

は (fact 10) の継続は (lambda (a) (* 2 a)) になる、と言えるのか。

なるほど

基本的に scheme の手続きって単一の値を戻すので継続の引数は基本一つで良いのか。でも多値を戻す手続きを待つ場合ってどうなるんでしょ。と、言いつつここは現時点ではスルー。
# 件の文書には沢山の値を受け取る失敗継続の例もあって
# 継続の引数は基本一つ、って言い方微妙かも

とりあえず上記の

gosh> (fact/cps 5 identity)

を手動で展開してみます。

(fact/cps 5 identity)
;; 5 の階乗の結果を待っているのは identity
(fact/cps 4 (lambda (a) (identity (* 5 a))))
;; 4 の階乗の結果を待っているのは (lambda (a) (* 5 a))
(fact/cps 3 (lambda (a) ((lambda (a) (identity (* 5 a))) (* 4 a))))
;; 3 の階乗の結果を待っているのは (lambda (a) (* 4 a))

長くなりそげなので略。コメントも微妙ですが ...
# 本来処理結果を待っているのは cont 全体のはず

call-with 系

詳細はテキスト見てもらうとして、要点は以下。

call-with系の関数は、一般化して言えば、fooの素からfooを生成し、procにfooを渡して呼び出す関数ということになるだろう。

call/procedure の実装例が以下

(define (call/procedure lambda-expr proc)
  (proc lambda-expr))

call/procedure は

lambda式からprocedureを生成し、procにprocedureを渡して呼び出す

とある。定義は簡単なんですが、例えば (* 2 (fact 10)) だとどうなるか、な例もある。

(call/procedure (lambda (a) (* 2 a)) (lambda (p) (p (fact 10))))

置き換えたら以下

((lambda (p) (p (fact 10))) (lambda (a) (* 2 a)))

なんとなく継続の風味が漂ってます。

と、言いつつ

call/continuation-procedure の定義も call/procedure とさほど変わらず。

(define (call/continuation-procedure cont proc)
  (proc cont))

_継続手続きなんてのはただの関数なんだから当然だ_との事。なんとなく感じたのは、proc/procedure も proc/continuation-procedure も渡す引数マターなんだろな、という事。あ、これも call/procedure の項で書いてあるな。

call/cc

むむ。

(* 2 (fact 10))

は call/continuation-procedure 使うと、って直上にありますな。

(call/continuation-procedure (lambda (a) (* 2 a)) (lambda (p) (p (fact 10))))

p は cont で書き換えられてますがスルー。えーと、fact/cps がこれを使って再定義されてるな。

(define (fact/cps n cont)
  (call/continuation-procedure cont (lambda (c) (c (fact n)))))

ええと、(fact/cps 5 identity) がどう展開されるか、というと

((lambda (c) (c (fact 5))) identity)
;;; (identity (fact 5))

うーん。そろそろ佳境というか核心。とりあえず一旦エントリ投入。