コンピュータ サイエンスでは、制御フロー(または制御のフロー) とは、命令型プログラムの個々のステートメント、命令、または関数呼び出しが実行または評価される順序のことです。明示的な制御フローを重視する点が、命令型プログラミング言語と宣言型プログラミング言語の違いです。
命令型プログラミング言語では、制御フロー ステートメントは、2 つ以上のパスのうちどれに従うかを選択するステートメントです。非厳密な関数型言語では、同じ結果を達成するための関数と言語構造が存在しますが、通常は制御フロー ステートメントとは呼ばれません。
一連のステートメントは、通常、ブロックとして構造化され、グループ化に加えて、語彙のスコープも定義します。
割り込みとシグナルは、サブルーチンに似た方法で制御フローを変更できる低レベルのメカニズムですが、通常は、インライン制御フロー ステートメントの実行ではなく、外部刺激またはイベント (非同期で発生する可能性があります) への応答として発生します。
マシン言語またはアセンブリ言語のレベルでは、制御フロー命令は通常、プログラム カウンタを変更することによって機能します。一部の中央処理装置(CPU)では、使用できる制御フロー命令は条件付きまたは無条件の分岐命令 (ジャンプとも呼ばれます) のみです。
カテゴリー
さまざまな言語でサポートされる制御フロー ステートメントの種類は異なりますが、その効果によって分類できます。
- 別のステートメントでの継続(無条件分岐またはジャンプ)
- 何らかの条件が満たされた場合にのみ一連のステートメントを実行する(選択 - つまり条件分岐)
- 条件が満たされるまで、一連のステートメントを 0 回以上実行します (つまり、ループ -条件分岐と同じ)
- 一連の遠隔ステートメントを実行し、その後制御フローが通常戻る(サブルーチン、コルーチン、継続)
- プログラムを停止し、それ以上の実行を防止する(無条件停止)
プリミティブ
ラベル
ラベルは、ソース コード内の固定位置に割り当てられた明示的な名前または番号であり、ソース コード内の他の場所に現れる制御フロー ステートメントによって参照される場合があります。ラベルはソース コード内の位置を示すものであり、それ以外の効果はありません。
行番号は、一部の言語 ( BASICなど) で使用される名前付きラベルの代替です。行番号は、ソース コード内のテキストの各行の先頭に配置される整数です。行番号を使用する言語では、行番号の値が次の行ごとに増加する必要があるという制約が課されることがよくありますが、行番号が連続している必要はありません。たとえば、BASIC では次のようになります。
10 X = 3とします20 Xを印刷します
CやAdaなどの他の言語では、ラベルは識別子であり、通常は行の先頭に表示され、その直後にコロンが続きます。たとえば、C では次のようになります。
成功: printf ( "操作は成功しました。\n " );
ALGOL 60言語では、ラベルとして整数と識別子の両方が許可されていました (どちらもコロンで次のステートメントにリンクされています)。ただし、他のALGOLバリアントで整数が許可されているものはほとんどありませんでした。初期のFortranコンパイラでは、ラベルとして整数のみが許可されていました。Fortran-90 以降では、英数字のラベルも許可されています。
後藤
gotoステートメント (英語のgoとto を組み合わせたもので、それぞれ発音されます) は、無条件の制御転送の最も基本的な形式です。
キーワードは言語に応じて大文字または小文字になります が、通常は次のように記述されます。
ラベルへ移動
goto ステートメントの効果は、次に実行されるステートメントが、指定されたラベル (またはその直後) に現れるステートメントになるようにすることです。
Goto 文は、多くのコンピューター科学者、特にDijkstraによって有害であると考えられてきました。
サブルーチン
サブルーチンの用語はさまざまです。サブルーチンは、ルーチン、プロシージャ、関数 (特に結果を返す場合)、またはメソッド (特にクラスまたは型クラスに属する場合) と呼ばれることもあります。
1950 年代、コンピュータのメモリは現在の基準からすると非常に小さかったため、サブルーチンは主にプログラムのサイズを小さくするために使用されていました。コードは一度書かれると、その後プログラム内のさまざまな場所で何度も使用されました。
今日では、サブルーチンは、たとえば、あるアルゴリズムを分離したり、あるデータ アクセス メソッドを隠したりすることによって、プログラムをより構造化するためによく使用されます。多くのプログラマーが 1 つのプログラムで作業している場合、サブルーチンは作業を分割するのに役立つ モジュール性の 1 つです。
順序
構造化プログラミングでは、連続するコマンドの順序付けられたシーケンスは基本的な制御構造の 1 つと見なされ、反復、再帰、選択とともにプログラムの構成要素として使用されます。
最小限の構造化された制御フロー
1966年5月、BöhmとJacopiniはCommunications of the ACMに論文[1]を発表し、 gotoを含むあらゆるプログラムは、重複したコードやブール変数(真偽フラグ)の追加を伴う可能性のある、選択(IF THEN ELSE)とループ(WHILE条件DO xxx)のみを含むgotoのない形式に変換できることを示しました。後の著者は、選択をループ(およびさらに多くのブール変数)に置き換えることができることを示しました。
このようなミニマリズムが可能であるということは、必ずしもそれが望ましいということではありません。結局のところ、コンピューターは理論的には1 つのマシン命令(ある数値から別の数値を減算し、結果が負の場合は分岐する) のみを必要としますが、実際のコンピューターには数十、あるいは数百ものマシン命令があります。
Böhm と Jacopini の論文が示したのは、すべてのプログラムが goto フリーであるということです。他の研究では、1 つのエントリと 1 つの終了を持つ制御構造は、他の形式よりもはるかに理解しやすいことが示されました[要出典]。主な理由は、制御フローを中断することなく、どこでもステートメントとして使用できるためです。言い換えると、それらは構成可能でした。 (非厳密なプログラミング言語などのその後の開発、および最近では構成可能なソフトウェア トランザクションがこの戦略を引き継いでおり、プログラムのコンポーネントをさらに自由に構成できます。)
一部の学者は、ベーム-ヤコピニの結果に対して純粋主義的なアプローチを取り、ループの途中からの や のような命令でさえbreak、returnベーム-ヤコピニの証明には必要ないので悪い習慣であると主張し、そのため、すべてのループは単一の終了ポイントを持つべきだと主張しました。この純粋主義的なアプローチは、言語Pascal (1968 ~ 1969 年に設計) に体現されており、1990 年代半ばまで、学術界で入門プログラミングを教える際に好んで使用されていました。[2]ベーム-ヤコピニの定理を直接適用すると、構造化チャートに追加のローカル変数が導入される可能性があり、コードの重複も発生する可能性があります。[3] Pascal はこれらの問題の両方の影響を受けており、Eric S. Robertsが引用した実証研究によると、学生プログラマーは、配列内の要素を検索する関数の作成など、いくつかの単純な問題に対して Pascal で正しい解を定式化するのが困難でした。ロバーツが引用したヘンリー・シャピロによる1980年の研究では、パスカルが提供する制御構造のみを使用した場合、被験者のわずか20%が正しい解答を出したのに対し、ループの途中から戻りを記述することを許可された場合、この問題に対して誤ったコードを記述した被験者はいなかったことがわかった。[2]
実際の制御構造
制御構造を持つほとんどのプログラミング言語には、関係する制御構造の種類を示す最初のキーワードがあります。[説明が必要]次に、制御構造に最後のキーワードがあるかどうかによって言語が分かれます。
- 最終キーワードはありません: ALGOL 60、C、C++、Go、Haskell、Java、Pascal、Perl、PHP、PL/I、Python、PowerShell。このような言語では、ステートメントをグループ化する何らかの方法が必要です。
- 最終キーワード: Ada、APL、ALGOL 68、Modula-2、Fortran 77、Mythryl、Visual Basic。最終キーワードの形式は次のように異なります。
- Ada: 最後のキーワードは
end+スペース+ 最初のキーワードです (例:if...end if、loop...)end loop - APL: 最終キーワードは
:Endオプションで初期キーワード +:If...:End、:If...:EndIf、Select...:End、:Select...:EndSelect、ただし終了条件を追加する場合、終了キーワードは次のようになります。:Until - ALGOL 68、ミスリル: 最初のキーワードを逆に綴ります (例:
if...fi、case...)esac - Fortran 77: 最終キーワードは
END+ 初期キーワードです (例:IF...ENDIF、DO...)ENDDO - Modula-2:
ENDすべてに同じ最終キーワード - Visual Basic:すべての制御構造には独自のキーワードがあります。... ;
If... ; ... ; ...End IfForNextDoLoopWhileWend
- Ada: 最後のキーワードは
選択
If-then-(else) 文
条件式と条件構造は、プログラマーが指定したブール条件がtrue と評価されるか false と評価さ れるかに応じて異なる計算またはアクションを実行するプログラミング言語の機能です。
IF..GOTO非構造化言語に見られるフォームは、典型的なマシンコード命令を模倣しており、条件が満たされるとラベルまたは行番号にジャンプします (GOTO)。IF..THEN..(ENDIF)ジャンプに限定されるのではなく、単純なステートメントやネストされたブロックを THEN キー キーワードの後に続けることができます。これは構造化された形式です。IF..THEN..ELSE..(ENDIF)上記と同じですが、条件が偽の場合に 2 番目のアクションが実行されます。これは最も一般的な形式の 1 つで、多くのバリエーションがあります。終端を必要とするものもあればENDIF、そうでないものもあります。Cおよび関連言語では、終端キーワードまたは 'then' は必要ありませんが、条件を括弧で囲む必要があります。- 条件文は、他の条件文の中にネストすることができ、多くの場合、ネストされます。一部の言語では
ELSE、 とIFを組み合わせて にすることができ、複合文の最後に一連の または他の最終文をELSEIF置く必要がなくなります。ENDIF
あまり一般的ではないバリエーションとしては、次のものがあります。
- 初期のFortranなどの一部の言語[ a]には、数値が負、ゼロ、または正かどうかをテストする3元または算術ifがあります。
- 一部の言語には関数型のステートメントがあります
if。たとえば、Lisp の ですcond。 - 一部の言語には、C の三項演算子などの演算子形式のステートメントがあります。
if - Perl はC スタイル
ifをwhenおよびで補完しますunless。 - Smalltalk は、
ifTrue基本的な言語構造ではなく、条件文を実装するためにメッセージを使用しますifFalse。
case 文と switch 文
スイッチ ステートメント(またはケース ステートメント、または多方向ブランチ) は、指定された値と指定された定数を比較し、最初に一致した定数に従ってアクションを実行します。通常、一致が見つからない場合に実行されるデフォルトのアクション ("else"、"otherwise") が用意されています。スイッチ ステートメントを使用すると、ルックアップ テーブルなどのコンパイラ最適化が可能になります。動的言語では、ケースは定数式に限定されず、右側のシェル スクリプトの例のように、パターン マッチングにまで拡張されることがあります。この例では、デフォルトのケースが任意の文字列に一致するグロブとして実装されています。ケース ロジックは、 SQLのステートメントのように、関数形式で実装することもできます。
*)decode
ループ
ループとは、一度だけ指定されるが、連続して複数回実行される可能性のある一連の文のことです。ループ「内部」のコード (ループ本体、以下ではxxxとして表示) は、指定された回数、または一連の項目ごとに 1 回、または何らかの条件が満たされるまで、または無期限に実行されます。これらの項目の 1 つ自体もループである場合、それは「ネストされたループ」と呼ばれます。[4] [5] [6]
HaskellやSchemeなどの関数型プログラミング言語では、再帰プロセスと反復プロセスの両方が、構文上のループ構造ではなく、 末尾再帰プロシージャで表現されます。
カウント制御ループ
ほとんどのプログラミング言語には、ループを特定の回数繰り返す構造があります。ほとんどの場合、カウントは上向きではなく下向きに行うことができ、1 以外のステップ サイズを使用できます。
これらの例では、N < 1 の場合、プログラミング言語に応じて、ループ本体は 1 回実行されるか (I の値は 1)、まったく実行されないことがあります。
多くのプログラミング言語では、カウント制御ループでは整数のみが確実に使用できます。浮動小数点数はハードウェアの制約により不正確に表現されるため、次のようなループは
X := 0.1の場合、 0.1から1.0までステップ実行します。
丸め誤差やハードウェア、コンパイラのバージョンによっては、9 回または 10 回繰り返される場合があります。さらに、X の増分が繰り返し加算によって発生する場合、丸め誤差が蓄積されると、各反復における X の値が、予想されるシーケンス 0.1、0.2、0.3、...、1.0 と大幅に異なる可能性があります。
条件制御ループ
ほとんどのプログラミング言語には、何らかの条件が変化するまでループを繰り返す構造があります。いくつかのバリエーションでは、ループの開始時に条件をテストし、他のバリエーションでは終了時に条件をテストします。テストが開始時にある場合、本体は完全にスキップできますが、終了時にある場合、本体は常に少なくとも 1 回は実行されます。
コントロールブレークは、通常のループ内で値のグループの処理をトリガーするために使用される値の変更検出方法です。値はループ内で監視され、変更があるとプログラム フローがそれに関連付けられたグループ イベントの処理に転送されます。
DO UNTIL (ファイルの終わり)
IF 新しい郵便番号 <> 現在の郵便番号
display_tally(現在の郵便番号、郵便番号数)
現在の郵便番号 = 新しい郵便番号
郵便番号 = 0
終了
ジップカウント++
ループ
コレクション制御ループ
いくつかのプログラミング言語 (例: Ada、D、C++11、Smalltalk、PHP、Perl、Object Pascal、Java、C#、MATLAB、Visual Basic、Ruby、Python、JavaScript、Fortran 95以降) には、配列のすべての要素、またはセットやコレクションのすべてのメンバーを暗黙的にループできる特別な構造があります。
someCollection は[:eachElement |xxx] を実行します。
コレクション内のアイテムに対して、begin xxx endを実行します 。
foreach (アイテム; myCollection) { xxx }
someArray {xxx}をforeachする
foreach ($someArray を $k => $v として) { xxx }
コレクション<String> coll; for (String s : coll) {}
foreach ( myStringCollection内の文字列s ) { xxx }
someCollection | ForEach-Object { $_ }
forall ( インデックス = 最初:最後:ステップ... )
Scala にはfor 式があり、これはコレクション制御ループを一般化し、非同期プログラミングなどの他の用途もサポートします。Haskellには do 式と内包表記があり、これらを組み合わせることで Scala の for 式と同様の機能を提供します。
一般的な反復
forC のステートメントやCommon Lispのフォームなどの一般的な反復構造は、do上記の種類のループや、いくつかのコレクションを並列にループするなどの他のループを表現するために使用できます。より具体的なループ構造を使用できる場合は、式の目的がより明確になることが多いため、一般的な反復構造よりもこちらの方が好まれます。
無限ループ
無限ループは、プログラム セグメントが永久にループするか、エラーなどの例外的な条件が発生するまでループすることを保証するために使用されます。たとえば、イベント駆動型プログラム (サーバーなど)は永久にループし、発生したイベントを処理し、オペレーターによってプロセスが終了されたときにのみ停止する必要があります。
無限ループは、他の制御フロー構造を使用して実装できます。最も一般的なのは、非構造化プログラミングではジャンプバック (goto) ですが、構造化プログラミングでは、条件を省略するか、 のように明示的に true に設定することによって、終了しないように設定された不定ループ (while ループ) ですwhile (true) ...。一部の言語には、通常、不定ループから条件を省略することによって、無限ループ用の特別な構造があります。例としては、Ada ( loop ... end loop)、[7] Fortran ( DO ... END DO)、Go ( for { ... })、Ruby ( loop do ... end) などがあります。
多くの場合、ループ条件でループ内で変更されない変数が使用される条件制御ループでは、プログラミング エラーによって意図せずに無限ループが作成されます。
次の反復への継続
ループ本体内で、ループ本体の残りをスキップしてループの次の反復に進みたい場合があります。言語によっては、これを実行するcontinue(ほとんどの言語)、skip[ 8] cycle (Fortran)、next(Perl および Ruby) などのステートメントが用意されています。その効果は、最も内側のループ本体を途中で終了し、次の反復で通常どおり再開することです。反復がループの最後の反復である場合、その効果はループ全体を早期に終了することです。
現在の反復をやり直す
Perl [9]やRuby [10]redoなどの一部の言語には、現在の反復を最初から再開するステートメント
があります。
ループを再開
Rubyにはretryループ全体を最初の反復から再開する文があります。[11]
ループからの早期脱出
カウント制御ループを使用してテーブルを検索する場合、必要な項目が見つかったらすぐに検索を停止することが望ましい場合があります。一部のプログラミング言語では、break(ほとんどの言語)、Exit(Visual Basic)、last(Perl) などのステートメントが提供されており、その効果は、現在のループを直ちに終了し、そのループの直後のステートメントに制御を移すことです。早期終了ループの別名はloop-and-a-halfです。
次の例は、ループの早期終了と途中でテストが行われるループの両方をサポートするAdaで実行されています。両方の機能は非常に似ており、両方のコード スニペットを比較すると違いがわかります。早期終了はifステートメントと組み合わせる必要がありますが、途中の条件は自己完結型の構造です。
Ada.Text IOを使用;
Ada.Integer Text IOを使用 ;
手順 Print_Squares はX : Integerです
。Read_Dataの開始: loop Ada.Integer Text IO.Get ( X ) ; X = 0の場合Read_Dataを終了; Ada.Text IO.Put ( X * X ) ; Ada.Text IO.New_Line ; Read_Dataのループを終了; Print_Squaresの終了;
Pythonは、ループが早期に終了したかどうか(breakステートメントで)に応じて、ループでelse節を使用してコードの条件付き実行をサポートします。たとえば、
for n in set_of_numbers :
if isprime ( n ):
print ( "セットには素数が含まれています" )
break
else :
print ( "セットには素数が含まれていません" )
else上記の例の句は、内部の文ではなく、文にリンクされています。Pythonforのifループforとwhileループはどちらも、ループの早期終了が発生しなかった場合にのみ実行される else 句をサポートしています。
いくつかの言語はネストされたループからの脱出をサポートしています。理論界では、これらはマルチレベルブレークと呼ばれています。一般的な使用例の 1 つは、多次元テーブルの検索です。これは、bash [12]や PHP [13]のようにマルチレベルブレーク ( Nレベルからの脱出) を介して、または Go、Java、Perl [14]のようにラベル付きブレーク (脱出して指定されたラベルで続行) を介して実行できます。マルチレベルブレークの代替手段としては、単一のブレークと、別のレベルから脱出するためにテストされる状態変数、脱出先のレベルでキャッチされる例外、ネストされたループを関数内に配置し、ネストされたループ全体を終了させるために return を使用する、またはラベルと goto ステートメントを使用するなどがあります。C にはマルチレベルブレークが含まれておらず、通常の代替手段としては goto を使用してラベル付きブレークを実装します。[15] Python にはマルチレベルブレークまたは続行がありません。これは PEP 3136 で提案されましたが、追加された複雑さはまれな正当な使用に見合わないという理由で却下されました。[16]
マルチレベルブレークの概念は、理論計算機科学において興味深いものです。なぜなら、これは今日コサラジュ階層と呼ばれるものを生み出すからです。[17] 1973年、S.ラオ・コサラジュは、任意の深さのループからのマルチレベルブレークが許可されている限り、構造化プログラミングで追加の変数を追加しないことが可能であることを証明することにより、構造化プログラム定理を洗練しました。 [18]さらに、コサラジュは厳密なプログラムの階層が存在することを証明しました。つまり、すべての整数nに対して、深さnのマルチレベルブレークを含むプログラムが存在し、追加の変数を導入せずに深さn未満のマルチレベルブレークを含むプログラムに書き直すことはできません。[17]
ループされたステートメントを実行するサブルーチンから抜けて、ネストされたループとサブルーチンの両方から抜けることもできますreturn。複数のブレークに対して提案されている他の制御構造もありますが、これらは通常、例外として実装されます。
2004 年の教科書で、デイビッド・ワットはテネントのシーケンサーの概念を使って、マルチレベルブレークと return 文の類似点を説明しています。ワットは、エスケープシーケンサーと呼ばれるシーケンサーのクラスは「テキストで囲まれたコマンドまたはプロシージャの実行を終了するシーケンサー」と定義され、ループからの break (マルチレベルブレークを含む) と return 文の両方を包含していると指摘しています。ただし、一般的に実装されている return シーケンサーは (戻り) 値も渡すことができますが、現代の言語で実装されている break シーケンサーは通常渡すことができません。[19]
ループバリアントと不変式
ループバリアントとループ不変量はループの正しさを表現するために使用されます。[20]
実際には、ループ バリアントは、負でない初期値を持つ整数式です。バリアントの値は、ループの各反復中に減少する必要がありますが、ループの正しい実行中に負になることはありません。ループ バリアントは、ループが終了することを保証するために使用されます。
ループ不変条件は、最初のループ反復の前に真でなければならず、各反復の後も真のままでなければならないアサーションです。これは、ループが正しく終了すると、終了条件とループ不変条件の両方が満たされることを意味します。ループ不変条件は、連続する反復中にループの特定のプロパティを監視するために使用されます。
Eiffelなどの一部のプログラミング言語には、ループバリアントと不変条件のネイティブサポートが含まれています。その他の場合、サポートはアドオンであり、たとえばJavaのループステートメントに対するJava モデリング言語の仕様などです。
ループサブ言語
いくつかのLisp方言は、ループを記述するための広範なサブ言語を提供しています。初期の例としては、InterlispのConversional Lispが挙げられます。Common Lisp [21]は、そのようなサブ言語を実装するLoopマクロを提供しています。
ループシステム相互参照表
- a
while (true)は専用の言語構造ではないため、この目的では無限ループとしてカウントされません。 - a b c d e f g h C のループは一般的なループ構造であり、特にカウント用のループではありませんが、そのためによく使用されます。
for (init; test; increment) - a b c APL、C、C++、C# では、ラベルと goto を使用してディープ ブレークを実現できます。
- オブジェクトの反復処理 は PHP 5 で追加されました。
- a b c カウント ループは、増分リストまたはジェネレータ (たとえば、Python の) を反復処理することによってシミュレートできます。
range() - a b c d e 例外処理を使用することで、ディープブレークを実現できます。
- a
while関数をこれに対して使用できる ため、特別な構成はありません。 - a 特別な構造はありませんが、ユーザーは一般的なループ関数を定義できます。
- a C ++11標準では範囲ベースの forが導入されました。STLには、 STLコンテナを反復処理し、各要素に対して単項関数を呼び出すことができる
std::for_eachテンプレート関数があります。 [22]この機能は、これらのコンテナ上のマクロとして構築することもできます。[23] - カウント 制御ループは整数間隔にわたる反復によって実行され、終了の追加条件を含めることで早期に終了します。
- Eiffel は予約語をサポートしていますが、ループ制御ではなく例外処理
retryで使用されます。 - a Java モデリング言語(JML) 動作インターフェース仕様言語 が必要です。
- a ループバリアントは整数である必要があります。超限バリアントはサポートされていません。[1]
- D は無限コレクションと、それらのコレクションを反復処理する機能をサポートします。これには特別な構造は必要ありません。
- および手順を使用すると、深いブレーク を実現できます
GO TO。 - Common Lisp はジェネリックコレクション型の概念より古くから存在します。
構造化された非ローカル制御フロー
多くのプログラミング言語、特により動的なプログラミングスタイルを好む言語では、非ローカル制御フローの構成要素が提供されています。これにより、実行フローが特定のコンテキストから飛び出し、事前に宣言されたポイントで再開されます。条件、例外、継続は、3 つの一般的な非ローカル制御構成要素ですが、ジェネレーター、コルーチン、asyncキーワードなどのより特殊な構成要素も存在します。
条件
PL/I には、発生させたり、ON条件アクションによってインターセプトしたりできる 22 個の標準条件 (例: ZERODIVIDE SUBSCRIPTRANGE ENDFILE) があります。プログラマーは、独自の名前付き条件を定義して使用することもできます。
非構造化 ifと同様に、指定できるステートメントは 1 つだけなので、多くの場合、制御フローを再開する場所を決定するために GOTO が必要になります。
残念ながら、一部の実装では空間と時間の両方でかなりのオーバーヘッドがあったため (特に SUBSCRIPTRANGE)、多くのプログラマーは条件の使用を避けようとしました。
一般的な構文の例:
ON 条件 GOTO ラベル
例外
現代の言語には、(複数レベルの) break や return の使用に依存しない、例外処理用の特殊な構造化構造がありますGOTO。たとえば、C++ では次のように記述できます。
try { xxx1 // この中のどこかxxx2 // 使用: '''throw''' someValue; xxx3 } catch ( someClass & someId ) { // someClass の値をキャッチactionForSomeClass } catch ( someType & anotherId ) { // someType の値をキャッチactionForSomeType } catch (...) { // まだキャッチされていないものをキャッチactionForAnythingElse }
上記では、任意の数と種類の節を使用できます。特定の に一致するものcatchがない場合、一致するものが見つかるまで、またはメイン プログラムの終わりに達するまで、制御はサブルーチン呼び出しやネストされたブロックを遡って戻り、その時点で適切なエラー メッセージとともにプログラムが強制的に停止されます。
catchthrowcatch
C++ の影響により、 はcatch、Java や C# など、現在人気の他の言語でパターン マッチング例外ハンドラを宣言するために予約されているキーワードです。Ada などの他の言語では、このキーワードを使用してexception例外ハンドラを導入し、パターン マッチングに別のキーワード (Ada の場合) を使用することもありますwhen。AppleScript などのいくつかの言語では、例外ハンドラ構文にプレースホルダを組み込んで、例外が発生したときにいくつかの情報を自動的に抽出します。このアプローチは、AppleScript の次の構文で例示されていますon error。
try
エラー時にmyNumberをmyNumber / 0に設定 e数値n fからtまでの部分結果pr if ( e = "ゼロで割ることはできません" ) thenダイアログを表示"それはしないでください" end try
David Watt の 2004 年の教科書では、シーケンサーのフレームワークにおける例外処理も分析されています (この記事のループからの早期終了のセクションで紹介されています)。Watt は、算術オーバーフローやファイルが見つからないなどの入出力エラーで一般的に例証される異常な状況は、「低レベルのプログラム ユニットで検出されるが、ハンドラーは高レベルのプログラム ユニットに配置する方が自然である」タイプのエラーであると指摘しています。たとえば、プログラムにはファイルを読み取るための呼び出しが複数含まれている可能性がありますが、ファイルが見つからない場合に実行するアクションは、プログラムにとっての当該ファイルの意味 (目的) によって異なるため、この異常な状況の処理ルーチンを低レベルのシステム コードに配置することはできません。さらに、ワッツは、単一出口構造化プログラミングや(複数出口)リターン シーケンサーのように、呼び出し元でステータス フラグ テストを導入すると、「アプリケーション コードがステータス フラグのテストで乱雑になりがち」で、「プログラマーがステータス フラグのテストを忘れたり怠慢に省略したりする可能性がある」状況になると述べています。実際、ステータス フラグによって表される異常な状況は、デフォルトでは無視されます。 ワッツは、ステータス フラグ テストとは対照的に、例外はデフォルトの動作が逆であり、プログラムが何らかの方法で明示的に例外を処理しない限り、プログラムが終了すると指摘しています。例外を無視する明示的なコードを追加するなどです。これらの議論に基づいて、ワッツは、ジャンプ シーケンサーやエスケープ シーケンサーは、上記のセマンティクスを備えた専用の例外シーケンサーとしてはあまり適していないと結論付けています。[24]
Object Pascal、D、Java、C#、Python では、finally句を構造に追加できますtry。制御がどのように離れても、句try内のコードはfinally必ず実行されます。これは、処理が終了したときに高価なリソース (開いているファイルやデータベース接続など) を放棄する必要があるコードを書くときに便利です。
FileStream stm = null ; // C# の例try { stm = new FileStream ( "logfile.txt" , FileMode . Create ); return ProcessStuff ( stm ); // 例外がスローされる可能性があります} finally { if ( stm != null ) stm . Close (); }
このパターンはかなり一般的であるため、C# には特別な構文があります。
using ( var stm = new FileStream ( "logfile.txt" , FileMode . Create )) { return ProcessStuff ( stm ); // 例外がスローされる可能性があります}
-blockを抜けるとusing、コンパイラはstmオブジェクトが解放されることを保証し、変数をファイル ストリームに効果的にバインドしwithながら、ファイルの初期化と解放の副作用を抽象化します。Python のステートメントと Ruby のブロック引数は、File.open同様の効果のために使用されます。
上記の言語はすべて、標準の例外と、例外がスローされる状況を定義しています。ユーザーは独自の例外をスローできます。C++ では、 などの基本型を含むほぼすべての型をスローおよびキャッチできますintが、Java などの他の言語ではそれほど許容されません。
継続
非同期
C# 5.0 では、「直接スタイル」で 非同期 I/O をサポートするための async キーワードが導入されました。
発電機
ジェネレーター(セミコルーチンとも呼ばれる) を使用すると、通常はキーワード (yield の説明) を使用して、制御を一時的にコンシューマー メソッドに渡すことができますyield。async キーワードと同様に、これは「直接的なスタイル」でのプログラミングをサポートします。
コルーチン
コルーチンは、互いに制御を譲り合うことができる関数であり、スレッドを使用しない協調型マルチタスクの一種です。
プログラミング言語が継続またはジェネレーターのいずれかを提供する場合、コルーチンはライブラリとして実装できます。そのため、実際にはコルーチンとジェネレーターの区別は技術的な詳細です。
非ローカル制御フロー相互参照
提案された制御構造
1973年のDatamationのパロディー記事[31]で、R. Lawrence ClarkはGOTO文をCOMEFROM文に置き換えることができると示唆し、いくつかの面白い例を挙げています。COMEFROMは、 INTERCALという難解なプログラミング言語で実装されていました。
ドナルド・クヌースの1974年の論文「go to文による構造化プログラミング」[32]では、上記の制御構造ではカバーされていない2つの状況を特定し、これらの状況に対処できる制御構造の例を示しました。これらの構造は有用であるにもかかわらず、主流のプログラミング言語にはまだ採用されていません。
途中でテストを行うループ
1972年にダールは次のような提案をした。[33]
ループ ループ
xxx1 読み取り(文字);
whileテスト; while not atEndOfFile;
xxx2 書き込み(文字);
繰り返し; 繰り返し;
xxx1を省略すると、テストが先頭にあるループ (従来のwhileループ) になります。xxx2 を省略すると、テストが末尾にあるループになります。これは、多くの言語のdo whileループに相当します。whileを省略すると、無限ループになります。ここでの構成は、while チェックが中間にあるdoループと考えることができます。したがって、この単一の構成は、ほとんどのプログラミング言語の複数の構成を置き換えることができます。
この構造を持たない言語では、通常、同等の infinite-loop-with-break イディオムを使用してこれをエミュレートします。
(真)の間{
xxx1
if (テストではない)
break
2 ...
}
可能なバリエーションとしては、ループ内で複数のwhile test; を許可することですが、 exitwhen (次のセクションを参照) を使用すると、このケースをより適切にカバーできるようです。
Adaでは、上記のループ構造 ( loop - while - repeat ) は、中間にexit when句がある標準の無限ループ ( loop - end loop )を使用して表すことができます (次のセクションのexitwhenステートメントと混同しないでください)。
Ada.Text_IOを使用;
Ada.Integer_Text_IOを使用 ;
手順 Print_Squares はX : Integerです
。Read_Dataの開始: loop Ada.Integer_Text_IO.Get ( X ) ; X = 0の場合Read_Dataを終了; Ada.Text IO.Put ( X * X ) ; Ada.Text IO.New_Line ; Read_Dataのループを終了; Print_Squaresの終了;
ループに名前を付けること (この例ではRead_Dataなど) はオプションですが、複数のネストされたループの外側のループから抜け出すことができます。
複数の早期終了/ネストされたループからの終了
この構成は1974年にザーンによって提案された。[34]ここでは修正版を示す。
EventAまたはEventBまたはEventC の場合に終了します。
xxx
終了
イベントA: アクションA
イベントB: アクションB
イベントC: アクションC
終了;
exitwhen は、 xxx内で発生する可能性のあるイベントを指定するために使用され、その発生は、イベント名をステートメントとして使用して示されます。何らかのイベントが発生すると、関連するアクションが実行され、制御はendexit の直後に渡されます。この構造により、何らかの状況が適用されるかどうかの判断と、その状況に対して実行されるアクションが明確に区別されます。
exitwhenは概念的には例外処理に似ており、多くの言語では例外または同様の構造がこの目的で使用されます。
次の簡単な例では、2 次元テーブルで特定の項目を検索します。
見つかった場合または見つからない
場合は終了します。I := 1 ~ Nの場合、 J := 1 ~ Mの場合、 table[I,J] = targetの場合、見つかった場合は終了します。
ない;
終了
見つかりました: print ("アイテムはテーブル内にあります");
欠落: print ("アイテムがテーブル内にありません");
終了;
安全
ソフトウェアを攻撃する方法の1つは、プログラムの実行フローをリダイレクトすることです。スタックカナリア、バッファオーバーフロー保護、シャドウスタック、vtableポインタ検証など、さまざまな制御フロー整合性技術がこれらの攻撃に対する防御に使用されます。[35] [36] [37]
参照
注記
- ^ Fortran では、このステートメントは Fortran-90 では廃止されたとみなされ、Fortran 2018 では削除されました。
参考文献
- ^ Böhm, Jacopini. 「フロー図、チューリングマシン、および2つの形成規則のみを持つ言語」Comm. ACM、9(5):366-371、1966年5月。
- ^ ab Roberts, E. [1995]「ループ終了と構造化プログラミング:議論の再開」 Wayback Machineで2014-07-25にアーカイブされたACM SIGCSE速報、(27)1:268–272。
- ^ David Anthony Watt、William Findlay (2004)。プログラミング言語設計コンセプト。John Wiley & Sons。p. 228。ISBN 978-0-470-85320-7。
- ^ 「C 言語のネストされたループとその例」GeeksforGeeks 2019 年 11 月 25 日2024 年 3 月 14 日閲覧。
- ^ 「Python ネストされたループ」www.w3schools.com . 2024 年 3 月 14 日閲覧。
- ^ Dean, Jenna (2019-11-22). 「Nested Loops」. The Startup . 2024-03-14閲覧。
- ^ Ada プログラミング: 制御: 無限ループ
- ^ 「ループとは何か、そしてそれをどのように使用できるか?」。2020年7月28日時点のオリジナルよりアーカイブ。2020年5月25日閲覧。
- ^ "redo - perldoc.perl.org". perldoc.perl.org . 2020年9月25日閲覧。
- ^ 「control_expressions - Ruby 2.4.0 のドキュメント」。docs.ruby-lang.org 。2020 年 9 月 25 日閲覧。
- ^ 「control_expressions - Ruby 2.3.0 のドキュメント」。docs.ruby-lang.org 。2020 年 9 月 25 日閲覧。
- ^ 上級 Bash スクリプトガイド: 11.3. ループ制御
- ^ PHP マニュアル: 「break」
- ^ perldoc: 最後
- ^ comp.lang.c FAQ リスト · 「質問 20.20b」
- ^ [Python-3000] PEP 3136 の発表、Guido van Rossum
- ^ ab Kozen, Dexter (2008). 「Böhm-Jacopini の定理は命題的には偽である」。プログラム構築の数学(PDF) 。コンピュータサイエンスの講義ノート。第 5133 巻。pp. 177–192。CiteSeerX 10.1.1.218.9241。doi :10.1007/ 978-3-540-70594-9_11。ISBN 978-3-540-70593-2。
- ^ Kosaraju, S. Rao. 「構造化プログラムの分析」、Proc. Fifth Annual ACM Syrup. Theory of Computing、(1973 年 5 月)、240-252。また、J. Computer and System Sciences、9、3 (1974 年 12 月) にも掲載。Knuth , Donald (1974) による引用。「go to ステートメントによる構造化プログラミング」。Computing Surveys . 6 (4): 261–301。CiteSeerX 10.1.1.103.6084 . doi : 10.1145 /356635.356640。S2CID 207630080 。
- ^ David Anthony Watt、William Findlay (2004)。プログラミング言語設計コンセプト。John Wiley & Sons。pp. 215–221。ISBN 978-0-470-85320-7。
- ^ マイヤー、ベルトラン(1991年)。エッフェル:言語。プレンティスホール。pp.129-131。
- ^ 「Common Lisp LOOP マクロ」。
- ^ for_each. Sgi.com. 2010 年 11 月 9 日に取得。
- ^ 第 1 章 Boost.Foreach 2010 年 1 月 29 日にWayback Machineにアーカイブされました。Boost-sandbox.sourceforge.net (2009 年 12 月 19 日)。2010 年 11 月 9 日に取得。
- ^ David Anthony Watt、William Findlay (2004)。プログラミング言語設計コンセプト。John Wiley & Sons。pp. 221–222。ISBN 978-0-470-85320-7。
- ^ 「Asyncio: 非同期I/O: Python 3.10.2ドキュメント」。
- ^ 「Socketry/Async」。GitHub 。 2022年2月25日。
- ^ 「ジェネレーター - Rust の不安定な本」。
- ^ 「コロナ - 錆」.
- ^ 「Rust での非同期プログラミング入門」。
- ^ 「Jitsi Meet」. Storm-enroute.com . 2022年9月7日閲覧。
- ^ どこから来たのかがわからなければ、どこへ行けばいいのかもわからない。この(パロディーの)言語的革新は、すべての期待に応えるものだ。2018-07-16にWayback Machineでアーカイブ。R. Lawrence Clark著* Datamation、1973年12月より
- ^ Knuth, Donald E.「go to ステートメントを使用した構造化プログラミング」ACM Computing Surveys 6(4):261-301、1974 年 12 月。
- ^ Dahl & Dijkstra & Hoare、「構造化プログラミング」Academic Press、1972年。
- ^ Zahn, CT「自然なトップダウン構造化プログラミングのための制御ステートメント」、1974 年にパリで開催されたプログラミング言語シンポジウムで発表。
- ^ Payer, Mathias ; Kuznetsov, Volodymyr. 「CFI、CPS、CPI 特性の違いについて」nebelwelt.net . 2016 年 6 月 1 日閲覧。
- ^ 「Adobe Flash のバグ発見により新たな攻撃緩和方法が生まれる」Dark Reading 2015 年 11 月 10 日2016 年 6 月 1 日閲覧。
- ^ Endgame. 「Endgame、Black Hat USA 2016に出展」www.prnewswire.com (プレスリリース) 。 2016年6月1日閲覧。
さらに読む
- Hoare, CAR「パーティション: アルゴリズム 63」、「クイックソート: アルゴリズム 64」、および「検索: アルゴリズム 65」。Comm. ACM 4、321–322、1961 年。
