LISP - 错误处理
面向对象的错误处理- LISP条件系统
在Common Lisp的术语中,异常被称为条件。
事实上,条件比在传统编程语言的异常更为普遍,因为一个条件表示任何事件,错误与否,这可能会影响各级函数调用堆栈。
在LISP状态处理机制,处理的条件是用来警告信号(例如通过打印一个警告),而在调用堆栈的上层代码可以继续工作,这样的情况下以这样一种方式。
条件处理系统中LISP有三个部分:
-
信号的条件
-
处理条件
-
重启进程
处理一个条件
让我们处理由除零所产生的条件的例子,在这里解释这些概念。
需要处理的条件如下步骤:
-
定义条件 - “条件是一个对象,它的类表示条件的一般性质,其实例数据进行有关的特殊情况,导致被示意条件的细节信息”。
定义条件的宏用于定义一个条件,它具有以下语法:
(define-condition condition-name (error) ((text :initarg :text :reader text)))
:initargs 参数,新的条件对象与MAKE-CONDITION 宏,它初始化的基础上,新的条件下的插槽中创建的。
在我们的例子中,下面的代码定义的条件:
(define-condition on-division-by-zero (error) ((message :initarg :message :reader message)))
-
编写处理程序 - 条件处理程序是用于处理信号的条件在其上的代码。它一般写在调用该函数出问题的上级功能之一。当条件信号发生时,该信号转导机制中搜索基于所述条件的类合适的处理器。
每个处理程序包括:
-
类型说明符,它指示条件,它可以处理的类型
-
一个函数,它接受一个参数条件
当条件获得信号,该信号机制发现最近建立的处理程序与条件类型兼容,并调用它的函数。
宏处理程序的情况建立了一个条件处理程序。一个处理程序的 handler-case 形式:
(handler-case expression error-clause*)
那么,每个error从句的形式为:
condition-type ([var]) code)
-
-
重新启动阶段
这是真正从错误的代码中恢复程序,条件处理程序可以通过调用一个适当的重启处理的条件。重启代码一般是放置在中层或底层函数和条件处理程序被放置到应用程序的上层。
handler-bind宏允许提供一个重启功能,并允许继续在较低级的功能,无需解除函数的调用堆栈。换句话说,控制流将仍然处于较低水平的功能。
handler-bind的基本形式如下:
(handler-bind (binding*) form*)
其中每个绑定如以下列表:
-
条件类型
-
一个参数的处理函数
invoke-restart宏查找并调用具有指定名称作为参数最近绑定重启功能。
可以有多个重新启动。
-
示例
在这个例子中,我们演示了上述概念通过写一个名为划分功能函数,则会创建错误条件,如果除数参数为零。我们有三个匿名的功能,提供三种方式来出它 - 通过返回一个值1,通过发送一个除数2和重新计算,或通过返回1。
创建一个名为main.lisp一个新的源代码文件,并在其中输入如下代码:
(define-condition on-division-by-zero (error) ((message :initarg :message :reader message))) (defun handle-infinity () (restart-case (let ((result 0)) (setf result (division-function 10 0)) (format t "Value: ~a~%" result)) (just-continue () nil))) (defun division-function (value1 value2) (restart-case (if (/= value2 0) (/ value1 value2) (error 'on-division-by-zero :message "denominator is zero")) (return-zero () 0) (return-value (r) r) (recalc-using (d) (division-function value1 d)))) (defun high-level-code () (handler-bind ((on-division-by-zero #'(lambda (c) (format t "error signaled: ~a~%" (message c)) (invoke-restart 'return-zero))) (handle-infinity)))) (handler-bind ((on-division-by-zero #'(lambda (c) (format t "error signaled: ~a~%" (message c)) (invoke-restart 'return-value 1)))) (handle-infinity)) (handler-bind ((on-division-by-zero #'(lambda (c) (format t "error signaled: ~a~%" (message c)) (invoke-restart 'recalc-using 2)))) (handle-infinity)) (handler-bind ((on-division-by-zero #'(lambda (c) (format t "error signaled: ~a~%" (message c)) (invoke-restart 'just-continue)))) (handle-infinity)) (format t "Done."))
当执行代码,它返回以下结果:
error signaled: denominator is zero Value: 1 error signaled: denominator is zero Value: 5 error signaled: denominator is zero Done.
除了“系统状态”,如上文所讨论,普通的LISP还提供了各种功能,其可被称为信令错误。当信号实现相关处理错误。
LISP的错误信号功能
下表提供了常用功能的信令警告,休息,非致命和致命的错误。
用户程序指定一个错误信息(字符串)。该函数处理这个消息,并且可能/可能不会显示给用户。
错误信息应该通过应用的格式化功能进行构造,不应该在开头或结尾包含一个换行符,也无需指明错误,如LISP系统将根据其喜好的样式利用这些服务。
SL No. | 函数和说明 |
---|---|
1 |
error format-string &rest args 它标志着一个致命的错误。这是不可能从这种错误的继续;这样的错误将永远不会返回到其调用者。 |
2 |
cerror continue-format-string error-format-string &rest args 它发出错误信号,并进入调试器。但是,它允许程序从调试器解决错误之后继续。 |
3 |
warn format-string &rest args 它打印一条错误消息,但一般不会进入调试 |
4 |
break &optional format-string &rest args 它打印的消息,并直接进入调试器,而不允许拦截由编程错误处理设施的任何可能性 |
示例
在这个例子中,阶乘函数计算一个数阶乘;但是,如果参数为负,它抛出一个错误条件。
创建一个名为main.lisp一个新的源代码文件,并在其中输入如下代码:
(defun factorial (x) (cond ((or (not (typep x 'integer)) (minusp x)) (error "~S is a negative number." x)) ((zerop x) 1) (t (* x (factorial (- x 1)))))) (write(factorial 5)) (terpri) (write(factorial -1))
当执行代码,它返回以下结果:
120 *** - -1 is a negative number.