次: , 前: Catch and Throw, 上: Nonlocal Exits


9.5.2 catchthrowの例

catchthrowの使い方の1つは、 2重のループからの脱出です。 (ほとんどの言語では、これを『go to』で行うであろう。) ここでは、ijを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以外を返すと、 ただちに止まってijのリストを返します。 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もけっして評価されません。