14.6 状况 (Conditions)
在 Common Lisp 里,状况 (condition)包括了错误以及其它可能在执行期发生的情况。当一个状况被捕捉时 (signalled),相应的处理程序 (handler)会被调用。处理错误状况的缺省处理程序通常会调用一个中断循环 (break-loop)。但 Common Lisp 提供了多样的操作符来捕捉及处理错误。要覆写缺省的处理程序,甚至是自己写一个新的处理程序也是有可能的。
多数的程序员不会直接处理状况。然而有许多更抽象的操作符使用了状况,而要了解这些操作符,知道背后的原理是很有用的。
Common lisp 有数个操作符用来捕捉错误。最基本的是 error
。一个调用它的方法是给入你会给 format
的相同参数:
> (error "Your report uses ~A as a verb." 'status)
Error: Your report uses STATUS as a verb
Options: :abort, :backtrace
>>
如上所示,除非这样的状况被处理好了,不然执行就会被打断。
用来捕捉错误的更抽象操作符包括了 ecase
、 check-type
以及 assert
。前者与 case
相似,要是没有键值匹配时会捕捉一个错误:
> (ecase 1 (2 3) (4 5))
Error: No applicable clause
Options: :abort, :backtrace
>>
普通的 case
在没有键值匹配时会返回 nil
,但由于利用这个返回值是很差的编码风格,你或许会在当你没有 otherwise
子句时使用 ecase
。
check-type
宏接受一个位置,一个类型名以及一个选择性字符串,并在该位置的值不是预期的类型时,捕捉一个可修正的错误 (correctable error)。一个可修正错误的处理程序会给我们一个机会来提供一个新的值:
> (let ((x '(a b c)))
(check-type (car x) integer "an integer")
x)
Error: The value of (CAR X), A, should be an integer.
Options: :abort, :backtrace, :continue
>> :continue
New value of (CAR X)? 99
(99 B C)
>
在这个例子里, (car x)
被设为我们提供的新值,并重新执行,返回了要是 (car x)
本来就包含我们所提供的值所会返回的结果。
这个宏是用更通用的 assert
所定义的, assert
接受一个测试表达式以及一个有着一个或多个位置的列表,伴随着你可能传给 error
的参数:
> (let ((sandwich '(ham on rye)))
(assert (eql (car sandwich) 'chicken)
((car sandwich))
"I wanted a ~A sandwich." 'chicken)
sandwich)
Error: I wanted a CHICKEN sandwich.
Options: :abort, :backtrace, :continue
>> :continue
New value of (CAR SANDWICH)? 'chicken
(CHICKEN ON RYE)
要建立新的处理程序也是可能的,但大多数程序员只会间接的利用这个可能性,通过使用像是 ignore-errors
的宏。如果它的参数没产生错误时像在 progn
里求值一样,但要是在求值过程中,不管什么参数报错,执行是不会被打断的。取而代之的是, ignore-errors
表达式会直接返回两个值: nil
以及捕捉到的状况。
举例来说,如果在某个时候,你想要用户能够输入一个表达式,但你不想要在输入是语法上不合时中断执行,你可以这样写:
(defun user-input (prompt)
(format t prompt)
(let ((str (read-line)))
(or (ignore-errors (read-from-string str))
nil)))
若输入包含语法错误时,这个函数仅返回 nil
:
> (user-input "Please type an expression")
Please type an expression> #%@#+!!
NIL
脚注
[1] | 虽然标准没有提到这件事,你可以假定 and 以及 or 类型标示符仅考虑它们所要考虑的参数,与 or 及 and 宏类似。 |
[2] | 某些 Common Lisp 实现,当我们不在用户包下时,会在顶层提示符前打印包的名字。 |