Contents

Common-lisp Error and Exception Handling

这一节后半段需要CLOS的知识, 所以为了不产生疑惑,在读到define-condition的时候先去看看CLOS

Ignoring all errors, returning nil

有时候你知道函数会失败,并且你想忽略这个错误,可以使用ignore-errors

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
(ignore-errors
  (/ 3 0))
                                        ; in: IGNORE-ERRORS (/ 3 0)
                                        ;     (/ 3 0)
                                        ;
                                        ; caught STYLE-WARNING:
                                        ;   Lisp error during constant folding:
                                        ;   arithmetic error DIVISION-BY-ZERO signalled
                                        ;   Operation was (/ 3 0).
                                        ;
                                        ; compilation unit finished
                                        ;   caught 1 STYLE-WARNING condition
NIL
#<DIVISION-BY-ZERO {1008FF5F13}>

3/0 是一个错误,我们收到了警告。但是函数会正常返回nil,函数执行的状态被返回,失败了。

Catching any condition (handler-case)

ignore-errors是建立在handler-case上的。我们可以通过捕获err重写上面的例子,但是我们可以返回我们想返回的东西

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
(handler-case (/ 3 0)
  (error (c)
    (format t "We caught a condition.~&")
    (values 0 c)))
                                        ; in: HANDLER-CASE (/ 3 0)
                                        ;     (/ 3 0)
                                        ;
                                        ; caught STYLE-WARNING:
                                        ;   Lisp error during constant folding:
                                        ;   Condition DIVISION-BY-ZERO was signalled.
                                        ;
                                        ; compilation unit finished
                                        ;   caught 1 STYLE-WARNING condition
We caught a condition.
0
#<DIVISION-BY-ZERO {1004846AE3}>

可以看到我们返回了0 和 c handler-case的通用模板是

1
2
3
4
5
(handler-case (code that errors out)
  (condition-type (the-condition) ;; <-- optional argument
    (code))
  (another-condition (the-condition)
    ...))

我们也可以用t 来捕获条件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
(handler-case
    (progn
      (format t "This won`t work...~%")
      (/3 0))
  (t (c)
    (format t "Got an exception: ~a~%" c)
    (values 0 c)))
;; …
;; This won't work…
;; Got an exception: arithmetic error DIVISION-BY-ZERO signalled
;; Operation was (/ 3 0).
;; 0
;; #<DIVISION-BY-ZERO {100608F0F3}>

捕获一个特殊的条件

我们可以指定哪些条件需要处理

1
2
3
4
5
6
7
(handler-case (/ 3 0)
  (division-by-zero (c)
    (format t "Caught division by zero: ~a~%" c)))
;; …
;; Caught division by zero: arithmetic error DIVISION-BY-ZERO signalled
;; Operation was (/ 3 0).
;; NIL

这个和try/catch形式非常像,但是我们可以做更多的事情

handler-case vs handler-bind

handler-case和其他语言的try/catch非常像 handler-bind 用在哪些当错误出现时我们需要绝对控制的情况下。他允许我们交互的使用debugger和编程化的restart,

defining and make conditions

我们可以使用define-condition 定义condtions,使用make-condition 初始化他们

1
2
3
4
5
(define-condition my-division-by-zero (error)
  ())

(make-condition 'my-division-by-zero)
;; #<MY-DIVISION-BY-ZERO {1005A5FE43}>

定义condition时,最好给出更多的信息

1
2
3
4
5
(define-condition my-division-by-zero (error)
  ((dividend :initarg :dividend
             :initform nil
             :reader dividend)) ;; <-- we'll get the dividend with (dividend condition). See the CLOS tutorial if needed.
  (:documentation "Custom error when we encounter a division by zero.")) ;; good practice ;)