catch и throwОдин из способов использования catch и throw — это выход из двойного вложенного цикла. (В большинстве языков для этого используется goto.) Здесь мы вычисляем (foo i j) для i и j, изменяющихся от 0 до 9:
(defun search-foo ()
(catch 'loop
(let ((i 0))
(while (< i 10)
(let ((j 0))
(while (< j 10)
(if (foo i j)
(throw 'loop (list i j)))
(setq j (1+ j))))
(setq i (1+ i))))))
Если foo когда-либо вернёт не nil, мы немедленно останавливаемся и возвращаем список из i и j. Если foo всегда возвращает nil, catch завершается нормально, и значение равно nil, поскольку это результат while.
Вот два хитрых примера, немного отличающихся, показывающих две точки возврата одновременно. Сначала две точки возврата с одинаковой меткой hack:
(defun catch2 (tag)
(catch tag
(throw 'hack 'yes)))
⇒ catch2
(catch 'hack (print (catch2 'hack)) 'no) -| yes ⇒ no
Поскольку обе точки возврата имеют метки, совпадающие с throw, он переходит к внутренней точке, установленной в catch2. Следовательно, catch2 нормально возвращает значение yes, и это значение печатается. Наконец, вычисляется вторая форма тела во внешнем catch, которой является 'no, и она возвращается из внешнего catch.
Теперь изменим аргумент, передаваемый в catch2:
(catch 'hack (print (catch2 'quux)) 'no) ⇒ yes
У нас по-прежнему две точки возврата, но на этот раз только внешняя имеет метку hack; внутренняя вместо неё имеет метку quux. Следовательно, throw заставляет внешний catch вернуть значение yes. Функция print никогда не вызывается, и тело-форма 'no никогда не вычисляется.