📜  LISP-错误处理

📅  最后修改于: 2020-11-03 07:21:40             🧑  作者: Mango


在Common LISP术语中,异常称为条件。

实际上,条件比传统编程语言中的异常更笼统,因为条件表示任何出现,错误与否,这可能会影响函数调用堆栈的各个级别。

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

LISP中的条件处理系统包含三个部分:

  • 发出状况信号
  • 处理条件
  • 重新启动程序

处理条件

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

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

  • 定义条件-“条件是一个对象,其类指示条件的一般性质,并且其实例数据携带有关导致导致该条件被发出信号的特定情况的详细信息。”

    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))
)
  • 编写处理程序-条件处理程序是用于处理在其上发出信号的条件的代码。通常用调用错误函数的高级功能之一编写。发出条件信号时,信号机制会根据条件的类别搜索适当的处理程序。

    每个处理程序包括-

    • 类型说明符,指示它可以处理的条件类型
    • 一个函数,它的单个参数,该条件

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

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

(handler-case expression error-clause*)

其中,每个错误子句的形式为-

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.

除了如上所述的“条件系统”,Common LISP还提供了各种功能,可以调用这些功能来发出错误信号。但是,在发出信号时,错误的处理取决于实现。

LISP中的错误信令功能

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

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

错误消息应通过应用格式函数来构造,不应该在任开头或结尾包含一个字符,并且需要不指示错误,因为LISP系统将根据其优选风格照顾这些。

Sr.No. Function and Description
1

error format-string &rest args

It signals a fatal error. It is impossible to continue from this kind of error; thus error will never return to its caller.

2

cerror continue-format-string error-format-string &rest args

It signals an error and enters the debugger. However, it allows the program to be continued from the debugger after resolving the error.

3

warn format-string &rest args

it prints an error message but normally doesn’t go into the debugger

4

break &optional format-string &rest args

It prints the message and goes directly into the debugger, without allowing any possibility of interception by programmed error-handling facilities

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

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