次: Errors, 前: Catch and Throw, 上: Nonlocal Exits
catchとthrowの例catchとthrowの使い方の1つは、
2重のループからの脱出です。
(ほとんどの言語では、これを『go to』で行うであろう。)
ここでは、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は通常どおりに戻って、その値はnilです。
というのは、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で確立したものに戻ります。
したがって、catch2は値yesで通常どおり戻り、
この値が表示されます。
最後に、外側のcatchの2番目の本体フォーム、
つまり、'noが評価され、外側のcatchから戻ります。
今度は、catch2に指定する引数を変更してみます。
(defun catch2 (tag)
(catch tag
(throw 'hack 'yes)))
=> catch2
(catch 'hack
(print (catch2 'quux))
'no)
=> yes
ここでも2つの戻り位置がありますが、
今度は外側のものだけがタグhackです。
内側のものはタグquuxです。
したがって、throwにより、外側のcatchが値yesを返します。
関数printはけっして呼ばれず、
本体フォーム'noもけっして評価されません。