Next: , Previous: Catch and Throw, Up: 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もけっして評価されません。