通过宏继续使用常见的lisp - 关于Lisp中的实现

时间:2014-07-13 10:24:41

标签: lisp common-lisp continuations continuation-passing on-lisp

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的单独调用?这是怎么回事?

0 个答案:

没有答案