重启时的控制流程

问题描述 投票:0回答:1

我只是在玩弄 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
的递归调用仍会运行。不过,我预计此时重新启动会拦截控制流。由于递归调用+副作用,这只是一个有趣的案例吗?

common-lisp
1个回答
0
投票

确实,在

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
© www.soinside.com 2019 - 2024. All rights reserved.