継続渡し形式

継続渡し形式が私にはよく理解できなかったので、細かく追って見ました.
http://practical-scheme.net/wiliki/wiliki.cgi?Scheme%3A%E4%BD%BF%E3%81%84%E3%81%9F%E3%81%84%E4%BA%BA%E3%81%AE%E3%81%9F%E3%82%81%E3%81%AE%E7%B6%99%E7%B6%9A%E5%85%A5%E9%96%80
上のリンクでは、例として二つの階乗計算が示されています.

;; 階乗の普通の定義
(define (fact n)
  (if (= n 0)
      1
      (* n (fact (- n 1)))))

;; 継続渡し形式の階乗の定義
(define (fact/cps n cont)
  (if (= n 0)
      (cont 1)
      (fact/cps (- n 1) (lambda (a) (cont (* n a))))))
;; 次の計算は同じ
(* 2 (fact 10))
(fact/cps 10 (lambda (a) (* 2 a)))

頭の中で、二つの計算を考えてみたものの…(^p^)あうあうあーっていう感じになってしました.
とりあえず、普通のfactから展開していきます.

(fact 3)
;; (* 3 (* 2 (* 1 1))) ∴3!

次は、継続渡し形式のfact/cpsを追って見ます。

  • n = 3 のとき
(fact/cps 3 (lambda(a) (* 2 a)))
;;call -> (fact/cps 2 (lambda(a) (* 3 a)))
;;cont -> (lambda (a) (* 2 a))
  • n = 2 のとき contの内容が変更されていることに注意
(fact/cps 2 (lambda(a) (cont (* 3 a))))
;;call -> (fact/cps 1 (lambda(a) (cont (* 2 a))))
;;cont -> (lambda (a) (cont (* 3 a)))
  • n = 1 のとき
(fact/cps 1 (lambda(a) (cont (* 2 a))))
;;call -> (fact/cps 0 (lambda(a) (cont (* 1 a))))
;;cont -> (lambda (a) (cont (* 2 a)))
  • n = 0 のとき
(fact/cps 0 (lambda(a) (cont (* 1 a))))
;;call -> (cont 1)

基底条件にnが達したので、(cont 1)がコールされます。このcontは、[n=1]のcontです。
contに1をいれていますので、(lambda(1)…)と等価です。

  • n = 1の時のcont
(lambda (1) (cont (* 2 1)))
;; -> (lambda (1) (cont 2))

このcontは、[n=2]のcontでした。こっからは、一気に見てきます。

(lambda (2) (cont (* 2 3))
;;      -> (lambda (2) (cont 6)
;; cont -> (lambda (a) (* 2 a))
(lambda (6) (lambda (* 2 6))) 
;;     -> 12

計算をおってみると、3!×2であることがわかりました。継続渡し形式で階乗の計算をしています。

contの内容が変化することに対応できずに初期値の(lambda(a)...)の値を
contの値と勘違いしていたために、(^p^)あうあうあーになってしまいました.