2016年5月27日金曜日

継続と大域脱出

継続を使う例として空リストを受け取るなり脱出するflattenの例がよく挙げられるが、その動機がよくわからずやる気が出なかったので別の例を考えた。

引数の総積を求める関数はSchemeで次のように書ける。

(define (prod x . xs) (if (null? xs) x (* x (apply prod xs))))

(prod 1 2 3 4 5 6 7 8 9 10)
3628800

複雑にネストした数のリストにも適用したければ次のようにすればよかろう。
(define (prod-nest x . xs)
(let prod-aux ((y x) (ys xs))
((lambda (p) (if (list? y) (p (prod-aux (car y) (cdr y))) (p y)))
(lambda (a) (if (null? ys) a (* a (prod-aux (car ys) (cdr ys))))))))

(prod-nest '(1 ((2) 3 4 5) 6) 7 '((8 9) 10))
3628800


だが、総積は引数にゼロが発見され次第ゼロを返せばよろしい。さもなくばゼロに数を掛け続けるというシーシュポスもびっくりな行いをし続けなければならない!ゼロを発見次第ゼロを返すというのは、ゼロを発見次第継続にゼロを渡すということである。

これには次のように式を加えてやれば良い。ちゃんと中断していることがわかるように経過をprintした。
(define (prod-nest-cc x . xs)
(call/cc (lambda (cc)
(let prod-aux ((y x) (ys xs))
((lambda (p) (if (list? y) (p (prod-aux (car y) (cdr y)))
(begin (print y) (if (zero? y) (cc 0) (p y)))))
(lambda (a) (if (null? ys) a (* a (prod-aux (car ys) (cdr ys))))))))))

(prod-nest-cc '(1 ((2) 0 4 5) 6) 7 '((8 9) 10))
1
2
0
0

なお、即座にゼロを返すようにする、だけでは上手くいかない。
(define (prod-nest-x x . xs)
(let prod-aux ((y x) (ys xs))
((lambda (p) (if (list? y) (p (prod-aux (car y) (cdr y)))
(begin (print y) (if (zero? y) 0 (p y)))))
(lambda (a) (if (null? ys) a (* a (prod-aux (car ys) (cdr ys))))))))

(prod-nest-x '(1 ((2) 0 4 5) 6) 7 '((8 9) 10))
1
2
0
6
7
8
9
10
0
あくまで同一階層内の脱出に止まっていることがわかる。

0 件のコメント:

コメントを投稿