Next: , Up: Nonlocal Exits   [Contents][Index]


10.6.1 明示的な非ローカル脱出: catchthrow

ほとんどの制御構造は、その構文自身の内部の制御フローだけに影響します。関数throwは、この通常のプログラム実行でのルールの例外です。これはリクエストにより非ローカル脱出を行ないます(他にも例外はあるがそれらはエラー処理用のものだけ)。throwcatchの内部で使用され、catchに制御を戻します。たとえば:

(defun foo-outer ()
  (catch 'foo
    (foo-inner)))

(defun foo-inner ()
  …
  (if x
      (throw 'foo t))
  …)

throwフォームが実行されると、対応するcatchに制御を移して、catchは即座にリターンします。throwの後のコードは実行されません。throwの2番目の引数はcatchのリターン値として使用されます。

関数throwは1番目の引数にもとづいて、それにマッチするcatchを探します。throwは1番目の引数が、throwで指定されたものとeqであるようなcatchを検索します。複数の該当するcatchがある場合には、最内のものが優先されます。したがって上記の例ではthrowfooを指定していて、foo-outer内のcatchが同じシンボルを指定しているので、(この間に他のマッチするcatchは存在しないと仮定するなら)そのcatchが該当します。

throwの実行により、マッチするcatchまでのすべてのLisp構文(関数呼び出しを含む)を脱出します。この方法によりletや関数呼び出しのようなバインディング構文を脱出する場合には、これらの構文を正常にexitしたときのように、そのバインディングは解消されます(Local Variablesを参照)。同様にthrowsave-excursion(Excursionsを参照)によって保存されたバッファーと位置を復元します。throwがスペシャルフォームunwind-protectを脱出した場合には、unwind-protectにより設定されたいくつかのクリーンアップも実行されます。

ジャンプ先となるcatch内にレキシカル(局所的)である必要はありません。throwcatch内で呼び出された別の関数から、同じようにに呼び出すことができます。throwが行なわれたのが、時系列的にcatchに入った後で、かつexitする前である限り、そのthrowcatchにアクセスできます。エディターのコマンドループから戻るexit-recursive-editのようなコマンドで、throwが使用されるのはこれが理由です。

Common Lispに関する注意: Common Lispを含む、他のほとんどのバージョンのLispは非シーケンシャルに制御を移すいくつかの方法 — たとえばreturnreturn-fromgo — をもつ。Emacs Lispはthrowのみ。cl-libライブラリーはこれらのうちいくつかを提供する。Blocks and Exits in Common Lisp Extensionsを参照のこと。

Special Form: catch tag body…

catchthrow関数にたいするリターン位置を確立する。リターン位置はtagにより、この種の他のリターン位置と区別される。tagnil以外の任意のLispオブジェクト。リターン位置が確立される前に、引数tagは通常どおり評価される。

リターン位置が効果をもつことにより、catchbodyのフォームをテキスト順に評価する。フォームが(エラーや非ローカル脱出なしで)通常に実行されたなら、bodyの最後のフォームの値がcatchからリターンされる。

bodyの実行の間にthrowが実行された場合、tagと同じ値を指定するとcatchフォームは即座にexitする。リターンされる値は、それが何であれthrowの2番目の引数に指定された値である。

Function: throw tag value

throwの目的は、以前にcatchにより確立されたリターン位置に戻ることである。引数tagは、既存のさまざまなリターン位置からリターン位置を選択するために使用される。複数のリターン位置がtagにマッチしたら、最内のものが使用される。

引数valuecatchからリターンされる値として使用される。

タグtagのリターン位置が存在しなければ、データ(tag value)とともにno-catchエラーがシグナルされます。