在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
的每个叶子的树t1
的代码使用此实现,我想知道调用restart
时会发生什么,特别是在t1
的叶子从A
(第一个元素)更改为B
(第二个元素)。调用restart
时,它只是从*saved*
弹出一个lambda函数,该lambda函数再次调用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
的单独调用?这是怎么回事?