当你需要一个尾调用优化,比如语言中不(必须)提供它但确实提供闭包的结构时,你可以使用蹦床来实现恒定的堆栈空间(权衡闭包对象的堆空间,课程)。这与您所要求的并不完全相同,但您可能会发现它很有用。在 Common Lisp 中很容易实现:
(defstruct thunk closure)
(defmacro thunk (&body body)
`(make-thunk :closure (lambda () ,@body)))
(defun trampoline (thunk)
(do ((thunk thunk (funcall (thunk-closure thunk))))
((not (thunk-p thunk)) thunk)))
要使用蹦床,您只需使用执行第一部分计算的 thunk 调用它。该闭包可以返回另一个 thunk 或结果。如果它返回一个 thunk,则由于它返回了初始堆栈帧,然后调用返回的 thunk 的闭包。例如,非可变映射车的实现可能如下所示:
(defun t-mapcar1 (function list)
(labels ((m (list acc)
(if (endp list)
(nreverse acc)
(thunk
(m (rest list)
(list* (funcall function (first list)) acc))))))
(m list '())))
当列表为空时,我们立即得到一个空列表:
CL-USER> (t-mapcar1 '1+ '())
NIL
如果不是,我们会返回一个重击:
CL-USER> (t-mapcar1 '1+ '(1 2))
#S(THUNK :CLOSURE #<CLOSURE (LAMBDA #) {10033C7B39}>)
这意味着我们应该用 trampoline 包装一个调用(这也适用于基本情况,因为 trampoline 传递非 thunk 值):
CL-USER> (trampoline (t-mapcar1 '1+ '()))
NIL
CL-USER> (trampoline (t-mapcar1 '1+ '(1 2)))
(2 3)
CL-USER> (trampoline (t-mapcar1 '1+ '(1 2 3 4)))
(2 3 4 5)
您的示例代码不足以作为说明性示例,但是
(defun fn (x)
(when x
(princ x)
(jump 'fn (cdr x)))
(rest))
会变成下面这样。提供从的return
提前终止fn
,并且返回的 thunk 值提供蹦床将为您调用的“下一个”计算。
(defun fn (x)
(when x
(princ x)
(return (thunk (fn (cdr x)))))
(rest))