catch
とthrow
の例 ¶2重にネストされたループから脱出する1つの方法は、catch
とthrow
を使うことです(これはほとんどの言語ではgoto
により行なわれるだろう)。ここではiとjを0から9に変化させて、(foo
i j)
を計算します:
(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
は通常どおりリターンして、その値はwhile
の結果であるnil
となります。
以下では2つのリターン位置を一度に表す、微妙に異なるトリッキーな例を2つ示します。まず同じタグhack
にたいして2つのリターン位置があります:
(defun catch2 (tag) (catch tag (throw 'hack 'yes))) ⇒ catch2
(catch 'hack (print (catch2 'hack)) 'no) -| yes ⇒ no
どちらのリターン位置もthrow
にマッチするタグをもつので内側のもの、つまりcatch2
で確立されたcatchへgotoします。したがってcatch2
は通常どおり値yes
をリターンして、その値がプリントされます。最後に外側のcatch
の2番目のbody、つまり'no
が評価されて外側のcatch
からそれがリターンされます。
ここでcatch2
に与える引数を変更してみましょう:
(catch 'hack (print (catch2 'quux)) 'no) ⇒ yes
この場合も2つのリターン位置がありますが、今回は外側だけがタグhack
で、内側はかわりにタグquux
をもちます。したがってthrow
により、外側のcatch
が値yes
をリターンします。関数print
が呼び出されることはなくbodyのフォーム'no
も決して評価されません。