Инструменты пользователя

Инструменты сайта


examination:flp:question34

Формы динамического прекращения вычислений: CATCH и THROW в РС – Лиспе.

CATCH и THROW являются еще одной парой специальных операторов, которые приводят к раскрутке стека. Вы будете использовать эти операторы еще реже, чем описанные выше – они являются наследием ранних версий Lisp, которые не имели в своем составе систему условий Common Lisp. Они не должны путаться с конструкциями try/catch и try/except из таких языков, как Java и Python.

CATCH и THROW являются динамическими аналогами конструкций BLOCK и RETURN-FROM. Так что вы используете CATCH для какого-то кода и затем используете THROW для выхода из блока CATCH с возвратом указанного значения. Разница заключается в том, что связь между CATCH и THROW устанавливается динамически – вместо лексически ограниченного имени, метка CATCH является объектом, называемым тегом catch, и любое выражение THROW вычисляется внутри динамического экстента CATCH, так что «выбрасывание» (throws) этого объекта будет приводить к раскрутке стека к блоку CATCH и приводить к немедленному возврату. Так что вы можете написать новые версии функций foo, bar и baz используя CATCH и THROW вместо BLOCK и RETURN-FROM:

(defparameter *obj* (cons nil nil)) ; некоторый произвольный объект

(defun foo ()
  (format t "Entering foo~%")
  (catch *obj*
    (format t " Entering ''CATCH''~%")
    (bar)
    (format t " Leaving ''CATCH''~%"))
  (format t "Leaving foo~%"))

(defun bar ()
  (format t "  Entering bar~%")
  (baz)
  (format t "  Leaving bar~%"))

(defun baz ()
  (format t "   Entering baz~%")
  (throw *obj* nil)
  (format t "   Leaving baz~%"))

Заметьте, что нет необходимости передавать замыкание вниз по стеку – baz может напрямую вызвать THROW. Результат будет таким же как и раньше.

CL-USER> (foo)
Entering foo
 Entering ''CATCH''
  Entering bar
   Entering baz
Leaving foo
NIL

Однако, CATCH и THROW слишком динамичные. И в CATCH, и в THROW, форма, представляющая тег, вычисляется, что означает, что ее значение в обоих случаях определяется во время выполнения. Так что, если некоторый код в bar присвоит новое значение *obj*, то THROW в baz не будет пойман в том же блоке CATCH. Это делает использование CATCH и THROW более тяжелым чем BLOCK и RETURN-FROM. Единственным преимуществом версии foo, bar и baz, которая использует CATCH и THROW, является то, что нет необходимости передавать замыкание вниз по стеку, для возврата из CATCH – любой код, который выполняется внутри динамического экстента CATCH может заставить вернуться к нему, путем «бросания» (FIXME throwing) нужного объекта.

В старых диалектах Lisp в которых не было ничего подобного системе условий Common Lisp, CATCH и THROW использовались для обработки ошибок. Однако, для того, чтобы сделать ее сопровождаемой, теги catch обычно были FIXME quoted symbols, так что вы могли понять, глядя на CATCH и THROW, где они будут перехвачены во время выполнения. В Common Lisp вы будете редко иметь нужду в использовании CATCH и THROW, поскольку система условий намного более гибкая.

examination/flp/question34.txt · Последние изменения: 2014/01/15 08:17 (внешнее изменение)