当前位置: 首页 > 文档资料 > LISP 中文教程 >

错误处理(Error Handling)

优质
小牛编辑
137浏览
2023-12-01

在通用LISP术语中,例外称为条件。

事实上,条件比传统编程语言中的异常更通用,因为condition表示任何可能影响各种级别的函数调用堆栈的事件,错误或不存在。

LISP中的条件处理机制以这样的方式处理这种情况,即条件用于发出警告(例如通过打印警告),同时调用堆栈上的上层代码可以继续其工作。

LISP中的状态处理系统有三个部分 -

  • 发出信号
  • 处理条件
  • 重启过程

处理条件

让我们举一个处理由零除条件引起的条件的例子,来解释这里的概念。

您需要采取以下步骤来处理条件 -

  • Define the Condition - “条件是一个对象,其类指示条件的一般性质,其实例数据包含有关导致条件发出信号的特定情况的详细信息”。

    define-condition宏用于定义条件,该条件具有以下语法 -

(define-condition condition-name (error)
   ((text :initarg :text :reader text))
)
  • 使用MAKE-CONDITION宏创建新条件对象,该宏根据:initargs参数初始化新条件的槽。

在我们的示例中,以下代码定义了条件 -

(define-condition on-division-by-zero (error)
   ((message :initarg :message :reader message))
)
  • Writing the Handlers - 条件处理程序是用于处理在其上发出信号的条件的代码。 它通常写在一个调用错误函数的高级函数中。 当发出条件信号时,信令机制根据条件的类搜索适当的处理程序。

    每个处理程序包括 -

    • 类型说明符,表示它可以处理的条件类型
    • 一个带有单个参数的函数,即条件

    当发出条件信号时,信令机制找到与条件类型兼容的最近建立的处理程序并调用其函数。

    handler-case建立一个条件处理程序。 处理程序案例的基本形式 -

(handler-case expression error-clause*)

其中,每个错误条款的形式 -

condition-type ([var]) code)
  • Restarting Phase

    这是实际从错误中恢复程序的代码,然后条件处理程序可以通过调用适当的重新启动来处理条件。 重启代码通常放在中级或低级函数中,条件处理程序放在应用程序的上层。

    handler-bind宏允许您提供重启功能,并允许您继续执行较低级别的功能而无需展开函数调用堆栈。 换句话说,控制流程仍将处于较低级别的功能。

    handler-bind的基本形式如下 -

(handler-bind (binding*) form*)

每个绑定都是以下列表 -

  • 条件类型
  • 一个参数的处理函数

invoke-restart宏以指定的名称作为参数查找并调用最近绑定的重启函数。

您可以进行多次重启。

例子 (Example)

在这个例子中,我们通过编写一个名为division-function的函数来演示上述概念,如果divisor参数为零,它将创建一个错误条件。 我们有三个匿名函数提供了三种方法 - 通过返回值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.

除了“条件系统”之外,如上所述,Common LISP还提供可以用于发信号通知错误的各种功能。 然而,在发信号时处理错误是依赖于实现的。

LISP中的错误信令功能

下表提供了常用功能,用于发出警告,中断,非致命和致命错误。

用户程序指定错误消息(字符串)。 这些功能处理此消息,可能会/可能不会将其显示给用户。

应该通过应用format函数来构造错误消息,不应该在开头或结尾包含换行符,并且不需要指示错误,因为LISP系统将根据其首选样式处理这些错误消息。

Sr.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

它打印消息并直接进入调试器,不允许任何可能被编程的错误处理设施拦截

例子 (Example)

在这个例子中,阶乘函数计算一个数的阶乘; 但是,如果参数为负数,则会引发错误条件。

创建一个名为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.