********** 制御構造 ********** ====================== 構造化プログラミング ====================== 初期の FORTRAN や COBOL は ``goto`` 文の乱用などによってプログラムの構造が不明瞭になりがちであった。1968年にダイクストラは、 Algol で使われていた順次・反復・分岐の基本構造を階層的に組み合わせることによって分かり易いプログラムが書けると主張し、その後のプログラミング言語に大きな影響を与えた。 .. admonition:: 初期のBasicとPascalでの繰り返し .. code-block:: basic 10 X=1 20 IF X>=1000 THEN GOTO 60 30 PRINT X 40 X=X*2 50 GOTO 20 60 END .. code-block:: pascal x = 1; while x<1000 do begin writeln(x); x = x*2 end; ========== 大域脱出 ========== 構造化プログラミングの考え方では、 ``goto`` 文のように、ブロック構造を無視して制御を移すのはよくないとされる。しかし、アルゴリズムによってはそういう書き方をしたい時もある。例えば、多くの言語では、 ``return`` 文を実行すると、繰り返しブロックの中であっても直ちに関数の実行を終了して呼び出し元へ戻る。 大域脱出(:index:`Non-local exits`)は、それを一般化して、複数回の繰り返しや関数呼び出しが重なっていても、すべて終了して最初に設定したところまで戻る。 .. admonition:: Ruby の Catch and throw .. code-block:: ruby def bar throw :a, 3 end def foo bar end catch(:a) do foo end ========== 例外処理 ========== 例外とは -------- プログラム実行中に起きた「普通ではない」ことで、何か特別な処理が必要な ことを例外(:index:`exception`)と言う。 * ハードウェア(およびOS)で検出される例外:ゼロによる除算、メモリ保護違反、ファイルの終端まで読んだ、など。 * ソフトウェアで検出される例外:配列の添字が範囲外である、など。 * ユーザが定義した例外 昔の言語では、実行中にエラーが起きると、プログラムはエラーメッセージを出して止まるだけであった。最近の言語では、エラーが起きても、例外処理を行なって実行を続けられるようになっているものが多い。また、エラー以外のものも同じように処理できる。 例外処理機構 ------------ 例外が起きた時に特別な処理をするプログラムの部分を例外ハンドラ(:index:`exception handler`)と言う。 言語に例外処理機構が備わっていなくても、例外処理ができないわけではない。例えば、すべての除算の前に除数が 0 であるか検査して、例外ハンドラを呼び出す条件分岐を書けばよい。しかし、例外処理機構には次のような利点がある。 * 自分で例外を検出し、例外処理へ制御を移すのは、大変面倒くさい。 * アルゴリズム中の「普通ではない」状況(特に大域脱出を伴うもの)を、きれいに書くことができる。 * 例外の伝播によって、あちこちの例外に対する処理をまとめて一箇所に書くことができる。 * 言語によっては、プログラムの品質を上げるため、プログラマに対して例外的状況にきちんと対応することを強制している。 .. admonition:: PL/I の例外処理 例外ハンドラは、実行文が書ける場所ならどこにでも書ける。例えば配列の添字が範囲を越えた時の例外 ``SUBSCRIPTRANGE`` のハンドラは次のように書く。 .. code-block:: none ON SUBSCRIPTRANGE BEGIN; PUT LIST ('ERROR') SKIP; END; ``ON`` は実行文なので、例外と例外ハンドラの束縛は動的である。つまり、 ``ON`` を実行してから、同じ例外名の ``ON`` を実行するまでは、その例外ハンドラが有効である。 例外処理を有効にするには、例外名を実行文の前に書く。無効にするには例外名の前に NO を付ける。例えば、配列の添字範囲を検査して例外を発生させたい場合は次のように書く。 .. code-block:: none (SUBSCRIPTRANGE): BEGIN; A = B(I); END; .. admonition:: Java の例外処理 * 例外はクラスによって表わされる。例外が発生すると、それに対応するインスタンスが作られ、例外ハンドラに渡される。 * 例外ハンドラは try-catch-finally 文で書く。try の部分をスコープとし、catch の部分で対応する例外のクラスとハンドラを指定する。finally の部分は、例外が発生したかどうかに関係なく最後に実行される。 * 例外のクラスが継承により階層構造をなしているので、ハンドラが対応する例外を階層的に指定できる。 * メソッドの ``throws`` 節で例外を指定すると、呼び出し元に伝播させることができる。 * 例外が起きる可能性があれば、ハンドラを書くか、呼び出し元に伝播させるか、どちらかをしなければならない。 .. admonition:: 課題14 :class: exercise Java では例外が起きる可能性があるところでは例外処理を書かなくてはいけないが、 ``Error`` と ``RuntimeException`` については書かなくてもよいのはどういう理由だと思われるか? :ref:`【解答例】 `