Явные нелокальные выходы: catch и throw
Большинство управляющих конструкций влияют только на поток управления внутри самой конструкции. Функция throw является исключением из этого правила нормального выполнения программы: она выполняет нелокальный выход по запросу. (Есть и другие исключения, но они предназначены только для обработки ошибок.) throw используется внутри catch, и переходит обратно к этому catch. Например:
(defun foo-outer ()
(catch 'foo
(foo-inner)))
(defun foo-inner ()
…
(if x
(throw 'foo t))
…)
Форма throw, если выполняется, передает управление прямо обратно соответствующему catch, который возвращается немедленно. Код, следующий за throw, не выполняется. Второй аргумент throw используется в качестве возвращаемого значения catch.
Функция throw находит соответствующий catch на основе первого аргумента: она ищет catch, чьим первым аргументом является eq к указанному в throw. Если существует более одного подходящего catch, то используется самый внутренний. Таким образом, в приведенном выше примере throw указывает на foo, а catch в foo-outer указывает на тот же символ, так что catch является подходящим (при условии, что нет другого соответствующего catch между ними).
Выполнение throw выходит из всех конструкций Lisp до соответствующего catch, включая вызовы функций. При выходе из конструкций связывания, таких как let или вызовов функций, связывания отменяются, точно так же, как при нормальном выходе из этих конструкций (см. Локальные переменные). Аналогично, throw восстанавливает буфер и позицию, сохраненные save-excursion (см. Экскурсии), и состояние сужения, сохраненное save-restriction. Также выполняет любые очищающие действия, установленные с помощью специальной формы unwind-protect при выходе из этой формы (см. Очистки).
throw не обязательно должен появляться лексически внутри catch, к которому он переходит. Он может быть вызван и из другой функции, вызываемой внутри catch. До тех пор, пока throw происходит хронологически после входа в catch и хронологически перед выходом из него, он имеет доступ к этому catch. Вот почему throw можно использовать в командах, таких как exit-recursive-edit, которые возвращаются в цикл команд редактора (см. Рекурсивный редактирование).
Примечание Common Lisp: Большинство других версий Lisp, включая Common Lisp, имеют несколько способов передачи управления не последовательно:
return,return-from, иgo, например. Emacs Lisp имеет толькоthrow. Библиотека cl-lib предоставляет версии некоторых из них. См. Блоки и выходы в Расширения Common Lisp.
- Специальная форма: catch метка тело…
-
catchустанавливает точку возврата для функцииthrow. Точка возврата отличается от других таких точек возврата меткой, которая может быть любым объектом Lisp, кромеnil. Аргумент метка оценивается обычно перед установлением точки возврата.После установления точки возврата
catchвычисляет формы тела в текстовом порядке. Если формы выполняются нормально (без ошибок или нелокального выхода), значение последней формы тела возвращается изcatch.Если во время выполнения тела выполняется
throw, указывая то же значение метка, то формаcatchзавершается немедленно; значение, которое она возвращает, — это то, что было указано в качестве второго аргументаthrow.
- Функция: throw метка значение
-
Цель
throw— вернуть значение из точки возврата, ранее установленной с помощьюcatch. Аргумент метка используется для выбора среди различных существующих точек возврата; он должен бытьeqк значению, указанному вcatch. Если несколько точек возврата соответствуют метка, используется самая внутренняя.Аргумент значение используется в качестве значения для возврата из этой
catch.Если точка возврата с меткой метка не установлена, то сигнализируется ошибка
no-catchс данными(tag value).
Copyright © 1990-1996, 1998-2022 Free Software Foundation, Inc.
Licensed under the GNU GPL license.
https://www.gnu.org/software/emacs/manual/html_node/elisp/Catch-and-Throw.html