11.7.2 Примеры для 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 никогда не вычисляется.