継続のお勉強
プログラミングGaucheのcall/ccでの継続(19.4-19.6)内容のお勉強。
break/next名前付き for-each
本のサンプルより単純な例(受け取るリストを可変ではなく1つに限定)で実装してみる。
(define-syntax for-each-ext (syntax-rules () [ (_ break next expr lis) (call/cc (lambda (break) (for-each (lambda (x) (call/cc (lambda (next) (expr x) ) ) ) lis) ) ) ] ) )
マクロの引数breakはfor-each-extマクロに対する継続となるので、この引数で渡したキーワードのオブジェクト(=継続手続き)を呼び出すと、そのbreakの引数をfor-each-extの結果として返す(つまり、呼ばれた段階でfor-each-extからbreakする)。
breakで抜ける(意味のない内容!!)例は以下のとおり。
(for-each-ext break next (lambda (x) (if (> x 3) (break #t) (print x) ) ) '(1 2 3 4 5) ) 1 2 3 #t
マクロの引数nextはfor-eachで呼び出される各要素への手続きに対する継続となるので、この引数で渡したキーワードのオブジェクト(=継続手続き)を呼び出すと、そのnextの引数を要素に対する手続きの結果として返す(つまり、呼ばれた段階でその要素への手続きのその後の処理をスキップしてfor-eachの繰り返しに制御に戻す)。
nextでスキップする(意味のない)例は以下のとおり。
(for-each-ext break1 next1 (lambda (x) (if (odd? x ) (next1 #f) (print x) ) ) '(1 2 3 4 5) ) 2 4 #<undef>
find-fond
あとちょっともどって19.4にのっているfind-fondの例
(define next #f) (define (process/cc elt seed) (call/cc (lambda (cont) (print "found: " elt) (cont (cons elt seed))) ) ) (define (breaker/cc proc break) (lambda (elt seed) (call/cc (lambda (cont) (set! next (lambda() (cont (proc elt seed)))) (break #f) ) ) ) ) (define (process elt seed) (print "found: " elt) (cons elt seed) ) (define (find-fold pred? proc seed lis) (cond [(null? lis) seed] [(pred? (car lis) ) (let ( (seed2 (proc (car lis) seed) ) ) (find-fold pred? proc seed2 (cdr lis))) ] [else (find-fold pred? proc seed (cdr lis))] ) ; cond ) ) (find-fold odd? process/cc '() '(1 2 3 4 5 6 7 8) ) #found: 1 #found: 3 #found: 5 #found: 7 (call/cc (lambda (cont0) (find-fold odd? (breaker/cc process cont0) '() '(1 2 3 4 5 6 7 8) ) ) ) (print (next)) #found: 1 (print (next)) #found: 1
process/ccのcall/cc内手続きの引数は、この例ではfind-fond4行目のprocの呼び出しに対する継続となっているので、call/ccを使わないで値を返す場合とやっていることは同じということになる。
breaker/ccのcall/cc内手続きに引数も、同じくfind-fond4行目のprocの呼び出しに対する継続であるが、こちらは、その継続オブジェクトをnext変数に代入して、proc呼び出し時の継続には戻らない。
このbreaker/ccに渡されるbreak引数には継続オブジェクトが渡されるべき引数で、この継続オブジェクトを呼び出すことにより、(find-foldでの繰り返しへは戻らずに、find-fondの呼び出しもとの)
この継続へ戻ることになる。