LISP - обработка ошибок

В общей терминологии LISP исключения называются условиями.

Фактически, условия являются более общими, чем исключения в традиционных языках программирования, потому что condition представляет любое вхождение, ошибку или нет, которые могут повлиять на различные уровни стека вызовов функций.

Механизм обработки условий в LISP обрабатывает такие ситуации таким образом, что условия используются для сигнализации предупреждения (например, путем печати предупреждения), в то время как код верхнего уровня в стеке вызовов может продолжать свою работу.

Система обработки условий в LISP состоит из трех частей:

  • Сигнализация состояния
  • Обработка состояния
  • Перезапустить процесс

Обработка состояния

Давайте рассмотрим пример обработки условия, возникающего из условия деления на ноль, чтобы объяснить здесь концепции.

Вам необходимо предпринять следующие шаги для обработки условия -

  • Define the 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 Макрос находит и вызывает последнюю связанную функцию перезапуска с указанным именем в качестве аргумента.

У вас может быть несколько перезапусков.

пример

В этом примере мы демонстрируем вышеупомянутые концепции, написав функцию с именем функция-деление, которая создаст условие ошибки, если аргумент делителя равен нулю. У нас есть три анонимные функции, которые предоставляют три способа выйти из этого - путем возврата значения 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 строка формата и остальные аргументы

Это сигнализирует о фатальной ошибке. От такой ошибки невозможно продолжить; таким образом ошибка никогда не вернется к вызывающей стороне.

2

cerror продолжить строку формата ошибка строка формата и остальные аргументы

Он сигнализирует об ошибке и попадает в отладчик. Однако он позволяет продолжить выполнение программы из отладчика после устранения ошибки.

3

warn строка формата и остальные аргументы

выводит сообщение об ошибке, но обычно не попадает в отладчик

4

break& необязательная строка формата и остальные аргументы

Он печатает сообщение и отправляется прямо в отладчик, не допуская возможности перехвата программными средствами обработки ошибок.

пример

В этом примере функция факториала вычисляет факториал числа; однако, если аргумент отрицательный, возникает состояние ошибки.

Создайте новый файл исходного кода с именем 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.