在Lisp中,第 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
的每个叶子遍历一棵树t1
,使用这个实现,我想知道restart
调用时会发生什么,特别是在叶子t1
从A
(第一个元素)更改为B
(第二个元素)之后。当restart
被调用时,它只是简单地从 中弹出一个 lambda 函数*saved*
,然后该 lambda 函数再次调用dft-node
with (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)
,等等,直到 finally (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
?这里发生了什么?