продолжение в общем lisp макросами - относительно реализации в OnLisp
ВНа Лиспе, п. 267, Пол Грэм обеспечивает реализацию макроса передачи продолжения:
(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*
и что лямбда-функции вызывает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)
, По словам Грэма, последующие звонки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
? Что здесь происходит?