部分継続
こんなモノがあるなんて知らなんだ。でも call/cc でてきても緊張はしなくなったと思います。思いたい。
とは言えいきなり_待ち受けている継続_などという微妙な言い回しが出てきて微妙。全部 call/cc に置き換えて考えてみる? いや、それ以前に_call/cc 式の返りを待ち受けている継続と call/cc が取り出す継続は同じ_というソレも微妙。たとえば
(define k #f) (+ 1 2 (call/cc (lambda (cont) (set! k cont) 3)))
みたいなソレだと k には
(lambda (a) (PRINT-AND-NEXT-REPL (+ 1 2 a)))
が束縛されるはず。
gosh> (define k #f) k gosh> (+ 1 2 (call/cc (lambda (cont) (set! k cont) 3))) 6 gosh> (k 4) 7 gosh>
大丈夫だな。で、上記の call/cc の戻りを待っている継続も同じ、という事は分かる。ふむふむ。確かにね。でもそれ以降に書いてある事がよく分からん。
と言いつつよくよく読んでみると 2. の項に書いてある「call/pc 式を待ち受けている継続は (以下略」という部分がキモか。
(+ 1 2 (reset/pc (+ 3 4 (call/pc (lambda (cont) (cont 5))))))
上記の例だと call/pc を待ち受けている継続が
(lambda (a) (PRINT-AND-NEXT-REPL (+ 1 2 a)))
なので call/pc な式を
(call/pc (lambda (cont) (cont) 5))
とかヤッてしまうと式全体の値が 8 になるような事があるのか。おそらくは
- call/pc と reset/pc はセット
- 外側に reset/pc を置いてこの式の待ち受け継続が内側の call/pc の待ち受け継続になる
- call/pc が作る継続は call/cc と同じと考えて OK
以降も複雑だけどなんとかおいかける事はできているみたい。環境破壊実験なソレは別途投入予定とゆーコトでとりあえずエントリ投入。
環境破壊と部分継続
お題として以下のようなナニが提示されている
(define x 1) (define z 3) (define k #f) (+ x (reset/pc (+ z (call/pc (lambda (cont) (set! k cont) 2)) x)) z)
- reset/pc の待ち受け継続は (lambda (a) (PRINT-AND-NEXT-REPL (+ 1 a z))) で良いのかな
- call/pc の待ち受け継続は (lambda (a) (PRINT-AND-NEXT-REPL (+ 1 a z))) か
- call/pc が作る継続は (lambda (a) (PRINT-AND-NEXT-REPL (+ 3 a x))) か
一応合ってるみたい。順番としては
- (+ x reset/pc z) の評価
- reset/pc の評価
- (+ z call/pc x) の評価
- cont に設定されるのは (lambda (a) (PRINT-AND-NEXT-REPL (+ 3 a x)))
- call/pc は 2 を待ち受け継続に戻す
- (+ 1 2 3) を計算
という順ですか。ちょっと注意しないと普通に (+ 1 (+ 3 2 1) 3) ってヤリそう。で、k に束縛されている継続は (lambda (a) (PRINT-AND-NEXT-REPL (+ 3 a x))) なんで
(k 1) ;; => 5 (k 2) ;; => 6 (k 3) ;; => 7
となるのか。で、テキストでは x を変更してみる例と z を変更してみる例が出ている。あと次の例は
(define x 1) (define z 3) (define k #f) (+ x (reset/pc (+ z (call/pc (lambda (cont) (set! k cont) (cont 2))) x)) z)
なソレで
(set! x 10) (set! z 100)
したらどうなるか、というもの。最初の戻りは 10 なんですが、この状態で
(k 2)
を実行したらどうなるか。まず k を待ち受ける継続は
(lambda (a) (PRINT-AND-NEXT-REPL (+ 1 a z)))
あるいは k に束縛されている継続は
(lambda (a) (PRINT-AND-NEXT-REPL (+ 3 a x)))
なので、(k 2) したら
(+ 1 (+ 3 2 10) 100)
の結果が戻るんかいな。げ、違う。待ち受けなソレはそん時限定か。とほほほ。あくまでも k に束縛されているのは変わらないんだけど、待ち受けなソレは reset/pc で囲んである時だけに有効なのか。うーん。
復習がてら最後の複雑な例を。
(+ 1 2 (reset/pc (+ 3 4 (call/pc (lambda (cont) (cont (cont (cont 5))))))))
これはこれは。テキストは見ずに検討してみる。
- reset/pc の待受継続は (lambda (a) (PRINT-AND-NEXT-REPL (+ 1 2 a)))
- call/pc の待受継続は (lambda (a) (PRINT-AND-NEXT-REPL (+ 1 2 a)))
- call/pc が生成する継続は (lambda (a) (PRINT-AND-NEXT-REPL (+ 3 4 a)))
えーと、いきなり書いてしまえ
((lambda (a) (PRINT-AND-NEXT-REPL (+ 1 2 a))) ((lambda (a) (PRINT-AND-NEXT-REPL (+ 3 4 a))) ((lambda (a) (PRINT-AND-NEXT-REPL (+ 3 4 a))) ((lambda (a) (PRINT-AND-NEXT-REPL (+ 3 4 a))) 5))))
わははは。ワケワカらんぞ。結局こうなるのかな
(+ 1 2 (+ 3 4 (+ 3 4 (+ 3 4 5))))
29 が戻る、という事でどうか。やったぁ、ビンゴだし。以降は Kafua の例が出ているようですが、プログラミング Gauche でいっちゃん最後に用意されている章で色々見てみましょうね。なんかしらんが結構しゃんしゃん見れるようになってる模様。