Учебники

LISP — Обработка ошибок

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

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

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

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

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

Обработка условия

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

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

  • Определите условие — «Условие — это объект, чей класс указывает на общий характер условия, а данные экземпляра содержат информацию о деталях конкретных обстоятельств, которые приводят к сигналу условия».

    Макрос define-condition используется для определения условия, которое имеет следующий синтаксис:

Определите условие — «Условие — это объект, чей класс указывает на общий характер условия, а данные экземпляра содержат информацию о деталях конкретных обстоятельств, которые приводят к сигналу условия».

Макрос define-condition используется для определения условия, которое имеет следующий синтаксис:

(define-condition condition-name (error)
   ((text :initarg :text :reader text))
)
  • Новые объекты условий создаются с помощью макроса MAKE-CONDITION, который инициализирует слоты нового условия на основе аргумента : initargs .

Новые объекты условий создаются с помощью макроса 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 (binding*) form*)

Где каждая привязка представляет собой список следующего —

  • тип условия
  • функция-обработчик одного аргумента

Макрос invoke-restart находит и вызывает последнюю связанную функцию restart с указанным именем в качестве аргумента.

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

пример

В этом примере мы демонстрируем вышеприведенные концепции, написав функцию с именем Division-function, которая создаст условие ошибки, если аргумент делителя равен нулю. У нас есть три анонимные функции, которые обеспечивают три способа выхода из него — путем возврата значения 1, отправки делителя 2 и пересчета или возврата 1.

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

Live Demo

(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. Функция и описание
1

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

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

2

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

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

3

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

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

4

разрыв и необязательный формат-строки и остальные аргументы

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

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

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

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

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

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

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

разрыв и необязательный формат-строки и остальные аргументы

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

пример

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

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

Live Demo

(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))

Когда вы выполняете код, он возвращает следующий результат —