call/cc リハビリ (3)

昨晩、call/cc に入った所で腑に落ちない部分にぶち当たり、力尽きてました。

ええと

_続きの計算はいたるところにある。_という件。

(* 2 (fact 10))

の (fact 10) の続きの計算は (lambda (a) (* 2 a)) だ、という事から以下。

;; (* 2 (fact 10))
(call-with-continuation-procedure (lambda (a) (* 2 a))
                                  (lambda (cont) (cont (fact 10))))

call/continuation-procedure が定義されてればきちんと計算できる。

gosh> (define (call/continuation-procedure cont proc)
  (proc cont))
call/continuation-procedure
gosh> (call/continuation-procedure (lambda (a) (* 2 a)) (lambda (cont) (cont (identity 2))))
4
gosh> 

ここから call/cc に繋げるあたりがナニ。こんなコードが書いてある。

(* 2 (call-with-continuation-procedure (lambda (* 2 a))
                                       (lambda (cont) (cont (fact 10)))))

上記はさらに * 2 されます。ここが微妙だったんですが、上記のコードは * 2 してあるから、継続は (lambda (a) (* 2 a)) が渡されます、という概念を説明するためのものなんですね。
で、上記みたいなナニが call/cc 使えばこう書ける、と。

(* 2 (call/cc (lambda (cont) (cont (fact 10)))))

引数 cont には (lambda (a) (* 2 a)) が格納されるはず。試してみます。ええと、(lambda (a) (* 2 a)) な継続を捕捉するにはどうすりゃ良いか。

gosh> (define f #f)
f
gosh> (set! f (* 2 (call/cc (lambda (cont) cont))))
*** ERROR: operation * is not defined between 2 and #<subr continuation>
Stack Trace:
_______________________________________
gosh>

げ。叱られた。

gosh> (* 2 (call/cc (lambda (cont) cont))
)
*** ERROR: operation * is not defined between 2 and #<subr continuation>
Stack Trace:
_______________________________________
gosh>

何してるのか。こうか。

gosh> (set! f (* 2 (call/cc (lambda (cont) (cont (identity 2)) cont))))
4
gosh> f
4
gosh>

これは f に計算結果が束縛されてるはず。こうするのか。

gosh> (* 2 (call/cc (lambda (cont) (set! f cont) (cont (identity 2)))))
4
gosh> f
#<subr continuation>
gosh> (f 3)
6
gosh>

なかなかに面倒ですね。

ここで PRINT-AND-NEXT-REPL が出てくるんですが、その前に上記で微妙だった * 2 なソレがナニ。具体的には_継続渡し形式への書き換えを試みてみる_というあたり。

;; call/ccの継続渡し形式への書き換えを試みてみる
(* 2 ((lambda (cont) (cont (fact 10)))
      (lambda (a) (* 2 a))))

ここでの疑問点は二つ。

  • * 2 以降の計算って?
  • * 2 が放置

という事で REPL が出てきます。あー成程。このあたりの記述って、call/cc が何をしているのか、という事を分かってもらおうとしているのか。便宜的に継続渡しなソレを使って説明しているだけなんですね。
んで、* 2 以降の計算は REPL ですよ、という事で以下な式が出てくる。

(* 2 ((lambda (cont) (cont (fact 10)))
      (lambda (a) (PRINT-AND-NEXT-REPL (* 2 a)))))

置き換えて以下。

(* 2 ((lambda (a) (PRINT-AND-NEXT-REPL (* 2 a))) (fact 10)))

さらに置き換えて以下。

(* 2 (PRINT-AND-NEXT-REPL (* 2 (fact 10))))

例えば以下な例で

gosh> (* 2 (call/cc (lambda (cont) (cont (identity 2)))))
4
gosh> 

先頭にある_* 2_は何処に行ったんだ、という疑問を払拭するために便宜的に PRINT-AND-NEXT-REPL が出てくる、と。上記のナニだと以下なカンジ?

(* 2 (PRINT-AND-NEXT-REPL (* 2 (identity 2))))

_* 2_の継続は REPL なので、という事で call/cc 自体はそこまで怪しい動作をしている訳ではないのだよ、という所にオチる模様。

ちょっと

休憩。
まだテキストの半分もいってないな。gdgd 読みつつゆっくりする予定。