Gauche:call/ccの使用例
call/cc の使用例
- 長いこと理解していなかった call/cc (call-with-current-continuation) ですが、
最近 R7RS や SRFI-34 の guard を調べていて、
だいぶ分かるようになったのでメモしておきます。
call/cc の使用例 1 (大域脱出)
(define (add x1 x2) (call/cc (lambda (k) (unless (number? x1) (k -10000)) (unless (number? x2) (k -20000)) (+ x1 x2)))) (add 1 2) ; => 3 (add "a" 2) ; => -10000 (add 1 "b") ; => -20000
- 上記の例で、call/cc を評価すると、まず call/cc の継続手続きを k にセットして、
手続き (lambda (k) ...) が呼び出されます。
そして (k X) を実行すると、X が call/cc の戻り値となり、
ジャンプして call/cc の続きの部分から処理が継続されます。
- したがって、例えば (add "a" 2) を実行すると、
引数の "a" が数値ではないため (k -10000) が実行されて
call/cc の戻り値が -10000 になり、
それがそのまま add の戻り値になるため、結果が -10000 になります。
call/cc の使用例 2 (動的環境の巻き戻し)
(define-syntax guard (syntax-rules () ((guard (var clause ...) e1 e2 ...) ((call/cc (lambda (guard-k) (with-exception-handler (lambda (condition) ((call/cc (lambda (handler-k) (guard-k (lambda () (let ((var condition)) (guard-aux (handler-k (lambda () (raise-continuable condition))) clause ...)))))))) (lambda () (call-with-values (lambda () e1 e2 ...) (lambda args (guard-k (lambda () (apply values args)))))))))))))
- 上記は、R7RS の 7.3 に載っている guard の一部です。
(これだけでは実行できませんが。。。)
- まず、((call/cc (lambda (guard-k) ...))) という部分ですが、
これは、内部から脱出して、動的環境を巻き戻すために存在しています。
その内側には (guard-k (lambda () X)) という記述があり、
これによって (lambda () X) が call/cc の戻り値になります。
そして、最初に戻って ((call/cc (lambda (guard-k) ...))) という部分をよく見ると、
call/cc がさらに一段括弧に囲まれているため、
この (lambda () X) が脱出後に呼び出されることになります。
- このようにすることで、内側の e1 e2 ... の部分に
dynamic-wind 等の動的環境の設定があった場合に、
確実に後始末をしてから次の処理を行う、という制御が行えます。
- 実際に上記 guard では、動的環境を抜けてから例外ハンドラを実行することで、
エラー処理を行いやすくしています(すでに後始末が済んでいるため)。
- また、上記 guard にはもう一箇所 ((call/cc (lambda (handler-k) ...)))
という部分があります。
これは、guard-k とは逆に、(handler-k (lambda () Y)) で
内側の動的環境に戻るために存在しています。
- これは、guard の例外ハンドラで例外を捕捉しなかった場合に、
元の動的環境に戻って、透過的に (すなわち、あたかも guard が存在しなかったかのように)
例外を上位に投げなおすために、このような処理となっています。
hamayama(2018/01/19 14:33:15 UTC)(2018/01/20 11:08:04 UTC)
(2018/01/21 03:03:13 UTC)(2018/01/22 02:08:39 UTC)
(2018/01/23 14:16:20 UTC)(2018/01/25 12:04:49 UTC)
(2018/02/06 12:10:14 UTC)(2018/05/02 15:27:16 UTC)