部分継続

こんなモノがあるなんて知らなんだ。でも 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 でいっちゃん最後に用意されている章で色々見てみましょうね。なんかしらんが結構しゃんしゃん見れるようになってる模様。