5.6 中止 (Aborts)
你可以使用 return
在任何时候离开一个 block
。有时候我们想要做更极端的事,在数个函数调用里将控制权转移回来。要达成这件事,我们使用 catch
与 throw
。一个 catch
表达式接受一个标签(tag),标签可以是任何类型的对象,伴随着一个表达式主体:
(defun super ()
(catch 'abort
(sub)
(format t "We'll never see this.")))
(defun sub ()
(throw 'abort 99))
表达式依序求值,就像它们是在 progn
里一样。在这段代码里的任何地方,一个带有特定标签的 throw
会导致 catch
表达式直接返回:
> (super)
99
一个带有给定标签的 throw
,为了要到达匹配标签的 catch
,会将控制权转移 (因此杀掉进程)给任何有标签的 catch
。如果没有一个 catch
符合欲匹配的标签时, throw
会产生一个错误。
调用 error
同时中断了执行,本来会将控制权转移到调用树(calling tree)的更高点,取而代之的是,它将控制权转移给 Lisp 错误处理器(error handler)。通常会导致调用一个中断循环(break loop)。以下是一个假定的 Common Lisp 实现可能会发生的事情:
> (progn
(error "Oops!")
(format t "After the error."))
Error: Oops!
Options: :abort, :backtrace
>>
译注:2 个 >>
显示进入中断循环了。
关于错误与状态的更多讯息,参见 14.6 小节以及附录 A。
有时候你想要防止代码被 throw
与 error
打断。借由使用 unwind-protect
,可以确保像是前述的中断,不会让你的程序停在不一致的状态。一个 unwind-protect
接受任何数量的实参,并返回第一个实参的值。然而即便是第一个实参的求值被打断时,剩下的表达式仍会被求值:
> (setf x 1)
1
> (catch 'abort
(unwind-protect
(throw 'abort 99)
(setf x 2)))
99
> x
2
在这里,即便 throw
将控制权交回监测的 catch
, unwind-protect
确保控制权移交时,第二个表达式有被求值。无论何时,一个确切的动作要伴随着某种清理或重置时, unwind-protect
可能会派上用场。在 121 页提到了一个例子。