R の継続

R に callCC という関数があって、

callCC                 package:base                 R Documentation

Call With Current Continuation

Description:

     A downward-only version of Scheme's call with current
     continuation.

と書かれていたので、試してみた。

http://www.stdio.h.kyoto-u.ac.jp/~hioki/gairon-enshuu/SchemeNotes/continuation.html を見ながらRで同じことをやってみた。

まず

(+ (* 1 2) (call/cc (lambda (c) (* 3 4)))) ;==> 14
(+ (* 1 2) (call/cc (lambda (c) (* 3 (c 4))))) ;==> 6

をほとんどそのまま書いて、

> `+`(`*`(1,2), callCC(function(c) `*`(3, 4)))
[1] 14
> `+`(`*`(1,2), callCC(function(c) `*`(3, c(4))))
[1] 6

関数を明示的に呼び出さないで、

> (1 * 2) + callCC(function(c) 3 * 4)
[1] 14
> (1 * 2) + callCC(function(c) 3 * c(4))
[1] 6

うまくいったので、次はこれ。

(+ (* 1 2) (call/cc (lambda (c) (set! cont c) (* 3 4))))
(cont 2) ;==> 2
> (1 * 2) + (callCC( function(c){assign("cont",c,envir=.GlobalEnv); 3 * 4}))
[1] 14
> cont(2)
以下にエラー cont(2) : 
戻るための関数がありません,トップレベルへジャンプします 

・・・だめだ。
よくわからないのでヘルプを見直すと
「A downward-only version of Scheme's call with current continuation.」
最初は下向きってのがよく分からなかったけど、どうやら callCC を呼びたしたとこより上では継続を呼び出せないみたい。(わけが分からない文だ)
けど、それなら大域脱出はできるよね、ってことで

(define (mul-cont x)
 (call/cc (lambda (escape) 
           (letrec ((mul-aux (lambda (y)
                              (display y)
                              (newline)
                              (let ((result
                                     (cond ((null? y) 1)
                                      ((= (car y) 0) (escape 0))
                                      (#t (* (car y) (mul-aux (cdr y)))))))
                               (display (list y result))
                               (newline)
                               result)
                             )))
            (mul-aux x)))))
(mul-cont '(1 2 3 4))
;(1 2 3 4)
;(2 3 4)
;(3 4)
;(4)
;()
;(() 1)
;((4) 4)
;((3 4) 12)
;((2 3 4) 24)
;((1 2 3 4) 24)
;24

(mul-cont '(1 0 3 4))
;(1 0 3 4)
;(0 3 4) ;; carが0のときに継続を呼び出して大域脱出する.
;0

をやってみた。

mulcont <- function(x){
	callCC( function(escape){
		mulaux <- function(y){
			cat(y, "\n");
			result <- 
				if(length(y)==0){1}
				else if(y[1]==0){escape(0)}
				else{ y[1] * mulaux(y[-1])}
			cat(c(y, result), "\n")	
			result
		}
	mulaux(x)
	})
}

> mulcont(c(1,2,3,4))
1 2 3 4
2 3 4
3 4
4

1
4 4
3 4 12
2 3 4 24
1 2 3 4 24
[1] 24
> mulcont(c(1,0,3,4))
1 0 3 4
0 3 4
[1] 0

よし、うまく動いた。

まだ継続自体を理解しきれてないから callCC の定義を見てもよく分からない。

> callCC
function (fun) 
{
	value <- NULL
	delayedAssign("throw", return(value))
	fun(function(v) {
		value <<- v
		throw
	})
}
<environment: namespace:base>

いろいろ動かしながら勉強してみよ。