我只是在玩弄 CL 条件并在(重新)阅读实用 Common Lisp 第 19 章后重新启动,并且在重新启动时遇到了一些我觉得有点令人费解的行为。
在下面的代码片段中,调用重新启动,该函数调用使用另一个参数发出条件信号的函数。
(define-condition no-fun-allowed-condition nil nil)
(defun low (value)
(format t "~&Before low.")
(if (equal value "fun")
(restart-case (signal 'no-fun-allowed-condition)
(some-restart () (low "no-fun")))
(format t "~&Running low with a non-fun value: '~A'" value))
(format t "~&After low."))
(defun mid ()
(format t "~&Before mid.")
(low "fun")
(format t "~&After mid."))
(defun high ()
(handler-bind
((no-fun-allowed-condition
#'(lambda (c)
(declare (ignorable c))
(invoke-restart 'some-restart))))
(mid)))
(high)
此打印
Before mid.
Before low.
Before low.
Running low with a non-fun value: 'no-fun'
After low.
After low.
After mid.
显然,由于重新启动,“Before low”会打印两次,但是“After low”运行两次呢?我能想到的唯一解释是,由于
handler-bind
不会展开调用堆栈,因此尽管重新启动,对 low
的递归调用仍会运行。不过,我预计此时重新启动会拦截控制流。由于递归调用+副作用,这只是一个有趣的案例吗?
确实,在
handler-bind
之后,执行会继续,就好像什么也没发生一样,因此达到了“低点之后”(位于 if
之外)。注意到它的顺序很有趣,所以让我们将其更改为 (format t "~&After low from ~a" value)
并再次运行 (high)
:
Before mid.
Before low.
Before low.
Running low with a non-fun value: 'no-fun'
After low from no-fun <---- within the handler
After low from fun <---- the first call continues
After mid.
如果处理程序中存在非本地退出,我们将不会看到第二个“低点之后”:
(some-restart ()
(low "no-fun")
(return-from low))
如果我们使用
handler-case
,这会倒回堆栈,并且不会到达第二个“低点之后”,但在这种情况下,我们需要通过在高级调用者中定义重新启动来重写示例。如果它留在 low
中,当我们想要使用它时它就不会存在。我上去:
(defun low (value)
(format t "~&Before low with ~a." value)
(if (equal value "fun")
(signal 'no-fun-allowed-condition)
(format t "~&Running low with a non-fun value: '~A'" value))
(format t "~&After low from ~a" value))
(defun mid ()
(format t "~&Before mid.")
(low "fun")
(format t "~&After mid."))
(defun high/case ()
(handler-case
(mid)
(no-fun-allowed-condition ()
(format t "~&!caught condition")
(invoke-restart 'some-restart))))
;; actual caller:
(restart-case (high/case)
(some-restart ()
(low "no fun")))
给出:
Before mid.
Before low with fun.
!caught condition
Before low with no fun.
Running low with a non-fun value: 'no fun'
After low from no fun