On Lisp では、p. 267 で、Paul Graham は、マクロを渡す継続の実装を提供します。
(setq *cont* #'identity)
(defmacro =lambda (parms &body body)
`#'(lambda (*cont* ,@parms) ,@body))
(defmacro =defun (name parms &body body)
(let ((f (intern (concatenate 'string
"=" (symbol-name name)))))
`(progn
(defmacro ,name ,parms
`(,',f *cont* ,,@parms))
(defun ,f (*cont* ,@parms) ,@body))))
(defmacro =bind (parms expr &body body)
`(let ((*cont* #'(lambda ,parms ,@body))) ,expr))
(defmacro =values (&rest retvals)
`(funcall *cont* ,@retvals))
t2
次のコードでは、 treeの各リーフのツリーをトラバースし、この実装を使用しています。特に、 のリーフが(最初の要素) から(2 番目の要素) に変更された後、 が呼び出されt1
たときに何が起こるのか疑問に思っています。が呼び出されると、単純に からラムダ関数がポップされ、そのラムダ関数が再度呼び出されます。しかし、この呼び出しは最も外側の のスコープ外で行われ、バインディングを担当していました。では、アウターによって導入されたバインディングはどのようにスコープ内にあるのでしょうか?restart
t1
A
B
restart
*saved*
dft-node
(cdr tree)
=bind
=bind
*cont*
*cont*
=bind
(setq *saved* nil)
(=defun dft-node (tree)
(cond ((null tree) (restart))
((atom tree) (=values tree))
(t (push #'(lambda () (dft-node (cdr tree))) *saved*)
(dft-node (car tree)))))
(=defun restart ()
(if *saved*
(funcall (pop *saved*))
(=values 'done)))
(setq t1 '(a (b (d h)) (c e (f i) g))
t2 '(1 (2 (3 6 7) 4 5)))
(=bind (node1) (dft-node t1)
(if (eq node1 'done)
'done
(=bind (node2) (dft-node t2)
(list node1 node2))))
最後の形式は次のように展開されます
(let ((*cont* (lambda (node1)
(if (eq node1 'done)
'done
(let ((*cont* (lambda (node2)
(list node1 node2))))
(dft-node t2))
(dft-node t1))))))
これにより、 が生成され(a 1)
ます。Graham によると、以降の への呼び出しはなどをまでrestart
生成し、その後の呼び出しでは、 などを 最終的に まで生成する必要があります。(a 2)
(a 5)
(b 1)
(b 2)
(g 5)
> (let ((node1 (dft-node t1)))
(if (eq? node1 ’done)
’done
(list node1 (dft-node t2))))
(A 1)
> (restart)
(A 2)
…
> (restart)
(B 1)
の後、確立され(a 1)
た のバインディングはもはや適切ではありません。これらの値への後続の呼び出しはどのように行われますか? のスコープは、への別の呼び出しにまだ適用されていますか? ここで何が起こっているのですか?*cont*
let
restart
let
restart