函数error用于抛出异常,带一个参数,可以是字符串,也可以像format一样拼字符串,也可以是一个异常对象,也可以是指向字符串或异常对
象的标识符,例如
(error "some error")
(setq x "some error") (error x)
(setq y '(a b c)) (error "error info : ~A" y)
接下来用handler-case捕捉异常,类似于try-catch
(defun foo() (error "just wrong"))
(handler-case (foo)
(error () "catch the error"))
运行后输出字符串catch the error,上面代码翻译成C++就是
try
{
foo();
}
catch (error)
{
return "catch the error";
}
除了基本的error类型,可以用define-condition定义新的异常类型
(define-condition log-entry-error (error)
((text :initarg :text :reader text)))
新类型名称是log-entry-error,它的父类是error,有一个参数text,该参数提供初始化方法和读方法。更多问题参考CLISP面向对象编程,定
义类的方法。
修改上面例子:
(defun foo() (error 'log-entry-error :text "just wrong"))
(handler-case (foo)
(log-entry-error (e) (text e)))
翻译成C++就是
try
{
foo();
}
catch (log-entry-error & e)
{
return e.text();
}
如果在catch中同时安插error和log-entry-error类型,会出什么样的结果呢?将谁放前面就会匹配谁
(handler-case (foo)
(error () "error")
(log-entry-error () "log-entry-error))
匹配error类型,返回字符串error。
可以用更底层的宏handler-bind来实现宏handler-case。handler-bind的使用格式如下
(handler-bind
((error-type-1 #'(lambda (x) (...)))
(error-type-2 #'(lambda (x) (...)))
(error-type-n #'(lambda (x) (...))))
(do something 1)
(do something 2)
(do something n))
也可以将lamda换成其它函数名,只要该函数接收一个参数。
使用示例:
(handler-bind
((error #'(lambda (x) (print "some error"))))
(/ 3 0))
值得注意的是,handler-bind虽然捕捉到了异常,但是并不会自动清除异常,上面例子打印了字符串some error后仍然触发了调试器。
可以用return-from从当前栈中返回,并清除异常,例如下面例子不会触发调试器:
(block xx
(handler-bind
((error #'(lambda (c) (return-from xx "the result"))))
(/ 3 0)))
也可以用go跳到下一处代码,并清除异常,例如下面例子不会触发调试器:
(tagbody
(handler-bind
((error #'(lambda (c)
(print "some error")
(go label))))
(/ 3 0))
label
(print "next statement"))
用宏handler-bind实现下面的代码
(handler-case (foo)
(error (var) "error")
(log-entry-error (var) "log-entry-error))
翻译成
(block xx
(handler-bind
((error #'(lambda (c)
(let ((var c))
(return-from xx "error"))))
(log-entry-error #'(lambda (c)
(let ((var c))
(return-from xx "log-entry-error")))))
(foo)))
如果没有任何捕捉异常的分支
(handler-case (foo))
翻译成
(block xx
(handler-bind
()
(foo)))
自己来实现宏handler.case
(defmacro handler.case (expression &rest handler-list)
`(block block-name
(handler-bind
,(gen-handler-list 'block-name handler-list)
,expression))
(defun gen-handler-list (block-name handler-list)
(cond ((eq handler-list nil) nil)
(t cons (gen-one-handler block-name (car handler-list))
(gen-handler-list block-name (cdr handler-list)))))
(defun gen-one-handler (block-name handler)
(let ((type (car handler))
(var (cadr handler))
(act (cddr handler)))
(cond ((eq var nil)
`(,type #'(lambda (c)
(return-from ,block-name ,@act))))
(t
`(,type #'(lambda (c)
(let ((,(car var) c))
(return-from ,block-name ,@act))))) )))