| ループ構造 |
|---|
ソフトウェアにおいて、制御フロー(または制御のフロー)は、実行が1つのコマンドから次のコマンドへとどのように進むかを表します。マシンコードや命令型プログラミング言語など、多くのコンテキストでは、コマンドが制御を別のポイントに渡す場合を除き、制御は順番に進みます(現在実行中のコマンドの直後にあるコマンドへ)。この場合、コマンドは制御フローコマンドとして分類されます。コンテキストに応じて、コマンドの代わりに他の用語が使用されます。たとえば、マシンコードでは、一般的な用語は命令であり、命令型言語では、一般的な用語はステートメントです。
命令型言語は制御フローを明示的に記述しますが、他のプログラミングパラダイムの言語では制御フローはそれほど重視されません。宣言型言語は、演算の順序を規定することなく、望ましい結果を指定します。関数型言語では、言語構成要素と関数の両方を用いてフローを制御しますが、通常は制御フロー文とは呼ばれません。
中央処理装置(CPU)の命令セットでは、制御フロー命令はプログラムカウンタを変更することが多く、無条件分岐(ジャンプ)または条件分岐のいずれかになります。代替アプローチとして、分岐の代わりに条件付きで命令を有効にするプレディケーションがあります。
割り込みやシグナルなどの非同期制御フロー転送は、割り込みが発生した場所に制御を返す前に、ハンドラーへの通常の制御フローを変更します。
ソフトウェアを攻撃する方法の一つは、実行フローをリダイレクトすることです。スタックカナリア、バッファオーバーフロー保護、シャドウスタック、vtableポインタ検証など、様々な制御フロー整合性技術がこれらの攻撃に対する防御に用いられます。[ 1 ] [ 2 ] [ 3 ]
制御フローはコード構造と密接に関連しています。制御フローは、構造と言語の実行規則によって定義された線に沿って流れます。この構造の一般的な概念は、ブロック構造に基づく順序付け、選択、反復処理に限定される構造化プログラミングと混同しないでください。
シーケンシャル実行は最も基本的な構造です。すべてのコードが本質的にシーケンシャルであるとは限りませんが、命令型コードはシーケンシャルです。
ラベルはソースコード内の位置を識別します。一部の制御フロー文はラベルを参照し、制御をラベル付き行にジャンプさせます。ラベルは位置を示す以外には、何の効果もありません。
一部の言語では、ラベルを数字に限定しており、これは行番号と呼ばれることもありますが、これはラベルではなく行の固有のインデックスを意味します。しかしながら、このような数値ラベルは、たとえ連続していなくても、ファイル内の上から下に向かって増加することが一般的に求められます。例えば、BASICでは次のようになります。
10 LET X = 3 20 PRINT X 30 GOTO 10多くの言語において、ラベルは英数字の識別子であり、通常は行頭に記述され、その直後にコロンが続きます。例えば、次のCコードはSuccess3行目にラベルを定義しており、このラベルはその後に続く最初の文(4行目)へのジャンプ先ポイントを示しています。
void f (ブール値ok ) {もし(大丈夫){成功へ進む;}戻る;成功:printf ( "OK" );}ほとんどの言語では、コードのシーケンスをブロックとして構成できます。制御文と組み合わせて使用すると、ブロックの先頭がジャンプ先となります。例えば、次のCコード(ブロックを区切るために中括弧を使用しています)では、done が偽の場合、制御文は1行目から4行目にジャンプします。
if (完了) {printf ( "すべて完了しました" );}それ以外{printf ( "まだ作業中" );}プログラミング言語には多くの制御コマンドが考案されています。このセクションでは、機能別に主要な構成要素について説明します。
関数は制御フローを提供します。つまり、呼び出されると関数のコードの先頭にジャンプし、完了すると制御が呼び出し元に戻ります。次のCコードでは、関数を呼び出すために制御が6行目から2行目にジャンプしますfoo()。その後、関数本体の実行(「Hi」の出力)が完了すると、制御は呼び出し後の7行目に戻ります。
void foo () {printf ( "こんにちは" );}void bar () {foo ();printf ( "完了" );}分岐コマンドは、コマンドを含むコード内のポイントからコマンドが指定するポイントに実行ポイントを移動します。
ジャンプ コマンドは、コード内の別のポイントに無条件に制御を分岐するもので、コードの流れを制御する最も基本的な形式です。
高級言語では、これは多くの場合goto文として提供されます。キーワードは言語によって大文字または小文字、あるいは1語または2語の場合がありますが、次のように記述されます。制御が goto 文に達すると、制御は指定されたラベルに続く文にジャンプします。goto 文は、ダイクストラをはじめとする多くのコンピュータ科学者によって有害であると考えられてきました。 goto label
条件文は、ブール式の値に基づいて制御を切り替えます。一般的な例には以下が含まれます。
then、これはif文と呼ばれることがあります。then、これは if-else 文と呼ばれます。ifおよびwhenで補完しますunless。ifTrue条件を実装します。ifFalse以下のPascalコードは単純なif-then-else文を示しています。Adaでも構文は同様です。
a > 0の場合writeln ( " yes " )、そうでない場合はwriteln ( " no " ) ;Cの場合:
if ( a > 0 ) { puts ( "はい" ); } else { puts ( "いいえ" ); }bashの場合:
if [ $a -gt 0 ] ; then echo "yes" else echo "no" fiPythonの場合:
a > 0 の場合: print ( "yes" ) else の場合: print ( "no" )Lispの場合:
( princ ( if ( plusp a ) "はい" "いいえ" ))多方向分岐は、一致する値に基づいて制御を切り替えます。一致する値が見つからない場合、通常、デフォルトのアクションが実行されます。switch文は、ルックアップテーブルなどのコンパイラ最適化を可能にします。動的言語では、ケースは定数式に限定されず、パターンマッチングにまで拡張される可能性があります。右側のシェルスクリプトの例では、*)デフォルトのケースを任意の文字列に一致するグロブとして実装しています。ケースロジックは、 SQLのdecode文 のように関数形式で実装することもできます。
次のPascalコードは比較的単純なswitch文を示しています。Pascalcaseでは の代わりに キーワードを使用しますswitch。
case someChar of 'a' : actionOnA ; 'x' : actionOnX ; 'y' , 'z' : actionOnYandZ ; else actionOnNoMatch ; end ;エイダでは:
case someCharは、' a 'の場合=> actionOnA 、' x 'の場合=> actionOnX 、' y ' | ' z 'の場合=> actionOnYandZ 、その他の場合=> actionOnNoMatch 、終了、です。Cの場合:
switch ( someChar ) { case 'a' : actionOnA ; break ; case 'x' : actionOnX ; break ; case 'y' : case 'z' : actionOnYandZ ; break ; default : actionOnNoMatch ; }Bashの場合:
case $someChar in a ) actionOnA ;; x ) actionOnX ;; [ yz ]) actionOnYandZ ;; * ) actionOnNoMatch ;; esacLispの場合:
( case some-char (( #\a ) action-on-a ) (( #\x ) action-on-x ) (( #\y #\z ) action-on-y-and-z ) ( else action-on-no-match ))Fortranの場合:
選択ケース( someChar )ケース( 'a' ) actionOnAケース( 'x' ) actionOnXケース( 'y' , 'z' ) actionOnYandZケースデフォルトactionOnNoMatch選択終了
ループとは、実行時状態に基づいて複数回実行される一連の文(ループ本体)です。ループ本体は、コレクションの各項目に対して1回実行されるか(確定反復)、条件が満たされるまで実行されるか(無期限反復)、または無限に実行されます。ループ本体内のループは、ネストされたループと呼ばれます。[ 4 ] [ 5 ] [ 6 ]ループの早期終了は、break文によってサポートされる場合があります。[ 7 ] [ 8 ]
HaskellやSchemeなどの関数型プログラミング言語では、再帰プロセスと反復プロセスの両方が、構文上のループ構造ではなく 末尾再帰プロシージャで表現されます。
比較的シンプルでありながら便利なループは、数値の範囲を反復処理します。単純な形式では、ある整数値から開始し、それよりも大きな整数値で終了し、その間の各整数値で反復処理を行います。多くの場合、増分は任意の整数値(負の値も可能で、より大きな値からより小さな値へとループします)にすることができます。
BASIC の例:
I = 1からN xxxまで、次のIPascal の例:
I := 1からNまで、begin xxx endを実行します。Fortran の例:
DO I = 1 、N xxx END DO多くのプログラミング言語では、整数のみが使用されるか、または確実に使用される可能性があります。浮動小数点はハードウェアの制約により不正確に表現されるため、次のループは丸め誤差、ハードウェア、コンパイラなどの様々な要因に応じて、9回または10回反復される可能性があります。さらに、Xの増分が繰り返し加算によって発生する場合、累積した丸め誤差により、各反復におけるXの値は、一般的に予想される0.1、0.2、0.3、…、1.0というシーケンスからかなり大きく異なる可能性があります。
X := 0.1の場合、 0.1から1.0までステップ実行します。
ループ構造の中には、条件が真になるまで繰り返し実行するものがあります。ループの開始時に条件をテストするものもあれば、終了時にテストするものもあります。テストが開始時に行われる場合、本体は完全にスキップされることがあります。終了時には、本体は必ず少なくとも1回は実行されます。
Visual Basic の例:
DO WHILE (テスト) xxxループPascal の例:
テストまでxxxを繰り返します。Cファミリーの事前テストの例:
while (テスト) { xxx }C ファミリの事後テストの例:
xxx while (テスト)を実行します。キーワードを使用しているもののfor、3つの部分からなるCスタイルのループは、数値ベースではなく条件ベースの構造です。2番目の部分である条件は各ループの前に評価されるため、ループは事前テストとなります。最初の部分は状態を初期化する場所であり、3番目の部分は次の反復のためのインクリメントですが、どちらの側面も他の場所で実行できます。次のCコードは、iを0からn-1まで反復する数値ループのロジックを実装しています。
for ( int i = 0 ; i < n ; ++ i ) { xxx }一部のループ構造はコレクションの項目を列挙し、各項目を反復します。
Smalltalk の例:
someCollection は次のように実行します: [ : eachElement | xxx ] 。Pascal の例:
コレクション内のアイテムの場合、begin xxx endを実行します。楽書の例:
foreach (アイテム; myCollection ) { xxx } TCL の例:
someArray { xxx }を foreach するPHP の例:
foreach ( $someArrayを$k => $vとして) { xxx }Java の例:
コレクション<文字列> coll ; for (文字列s : coll ) {}C# の例:
foreach ( myStringCollection内の文字列s ) { xxx }PowerShell で 'foreach' が 'ForEach-Object' のエイリアスである例:
いくつかのコレクション| foreach { $_ }Fortran の例:
forall (インデックス=最初:最後:ステップ... )Scalaには、コレクション制御ループを一般化するfor式があり、非同期プログラミングなどの他の用途もサポートしています。Haskellにはdo式と内包表記があり、これらを組み合わせることでScalaのfor式と同様の機能を提供します。
一般的なループ構造では、文や条件が重複して記述されるなど、コードの重複が発生することがあります。これは様々な理由で発生し、コードの重複を排除または最小限に抑えるための様々な解決策が提案されています。[ 11 ] goto文のような従来の非構造化解決策以外に、[ 12 ]一般的な構造化解決策としては、ループ内に条件文(if文)を配置する(条件文は重複する可能性があるが、文は重複しない)か、繰り返しロジックを関数でラップする(関数呼び出しは重複するが、文は重複しない)方法があります。[ 11 ]
よくあるケースとしては、ループの開始は常に実行されますが、最後の反復で終了がスキップされる場合があります。[ 12 ]これは、ダイクストラによって「 n回半」実行されるループと名付けられ、 [ 13 ]現在ではループ半問題と呼ばれています。[ 8 ]よくあるケースとしては、最初の部分でデータを読み取り、データの終了をチェックしてから 2 番目の部分でデータを処理する場合や、処理してから終了をチェックしてから次の反復の準備を行う場合などがあります。[ 12 ] [ 8 ]これらの場合、ループの最初の部分は 回実行されますが、2 番目の部分は 回しか実行されません。
この問題は少なくとも1967年からクヌースによって認識されており、ヴィルトはループの早期終了による解決法を提案した。[ 14 ] 1990年代以降、 break文を使った解決法が最も一般的に教えられている。 [ 8 ]
ループ文、条件分岐文、繰り返し
この解決策の微妙な点は、条件が通常のwhile条件とは逆になっていることです。while condition ... repeat を途中の exit で書き直すには、条件を逆にする必要があります。loop ... if not condition exit ... repeat。途中の制御構造に test があるループは、条件を逆にすることなく、明示的に半分のループのユースケースをサポートしています。[ 14 ]
ループ構造は、ループ文の後に別の反復処理を実行するか、実行を継続するかを決定する構造化された完了基準を提供します。しかし、多くの言語では、様々な非構造化制御フロー構造がサポートされています。
continue(最も一般的)、、skip[ 15 ] (cycle Fortran)、next(Perl および Ruby) などです。retryループ全体を最初の反復から再開する文があります。[ 18 ]早期終了はループ本体の後に制御をジャンプさせます[ 19 ] [ 8 ]break例えば、リストを検索する場合、項目が見つかった時点でループを停止できます。一部のプログラミング言語では、(ほとんどの言語)、Exit(Visual Basic)、last(Perl) などのステートメントが用意されています。
次の Ada コードでは、X が 0 のときにループが終了します。
loop Get ( X ); if X = 0 then exit ; end if ; DoSomething ( X ); end loop ;より慣用的なスタイルでは、次のように使用しますexit when。
loop Get ( X ); X = 0のときに終了; DoSomething ( X );ループを終了;Pythonはbreak、ループにelse節を使用することで、ループが(文によって)早期に終了したかどうかに応じてコードを条件付きで実行することをサポートしています。次のPythonコードでは、else節は内部の文ではなく文elseにリンクされています。Pythonのループとループはどちらもこのようなelse節をサポートしており、ループが早期に終了していない場合にのみ実行されます。 forifforwhile
for n in set_of_numbers : if isprime ( n ): print ( "セットには素数が含まれています" ) break else : print ( "セットには素数が含まれていません" )一部の言語では、ネストされたループからの脱出をサポートしています。理論界では、これらはマルチレベルブレークと呼ばれています。一般的な使用例の1つは、多次元テーブルの検索です。これは、bash [ 20 ]やPHP [ 21 ]のようにマルチレベルブレーク( Nレベルから脱出)を介して、またはAda、Go、Java、Rust、Perl [ 22 ]のようにラベル付きブレーク(脱出して指定されたラベルで続行)を介して行うことができます。マルチレベルブレークの代替手段には、別のレベルから脱出するためにテストされる状態変数と一緒に単一のブレーク、脱出先のレベルでキャッチされる例外、関数内にネストされたループを配置し、 return を使用してネストされたループ全体を終了させる、またはラベルとgoto文を使用するなどがあります。現在、CとC++のどちらにもマルチレベルブレークまたは名前付きループはなく、通常の代替手段はgotoを使用してラベル付きブレークを実装することです。[ 23 ]しかし、この機能の組み込みは提案されており[ 24 ]、Javaの構文に従ってC2Yに追加されました[ 25 ] 。Pythonには複数レベルのbreakやcontinueはありません。これはPEP 3136で提案されましたが、追加された複雑さはまれな正当な用途に見合わないという理由で却下されました[ 26 ] 。
マルチレベルブレークの概念は、今日コサラジュ階層と呼ばれるものを生み出すため、理論計算機科学において興味深いものです。[ 27 ] 1973 年にS. ラオ・コサラジュは、任意の深さのループからのマルチレベルブレークが許されている限り、構造化プログラミングで追加の変数を追加することを避けることが可能であることを証明して、構造化プログラム定理を洗練しました。 [ 28 ]さらに、コサラジュは、プログラムの厳密な階層が存在することを証明しました。つまり、すべての整数nに対して、深さnのマルチレベルブレークを含むプログラムが存在し、このプログラムは追加の変数を導入せずに、深さn未満のマルチレベルブレークを含むプログラムとして書き換えることはできません。[ 27 ]
2004年の教科書で、デイヴィッド・ワットはテネントのシーケンサーの概念を用いて、多段式breakとreturn文の類似性を説明しています。ワットは、エスケープシーケンサーと呼ばれるシーケンサーのクラスは「テキストで囲まれたコマンドまたはプロシージャの実行を終了するシーケンサー」と定義され、ループからのbreak(多段式breakを含む)とreturn文の両方を包含すると指摘しています。しかしながら、一般的な実装では、returnシーケンサーは(戻り)値も渡すことができますが、現代の言語で実装されているbreakシーケンサーは通常、(戻り)値を渡せません。[ 29 ]
1972年にダールは次のような構造を提案した。 [ 30 ]
ループループ xxx1 読み取り(文字); whileテスト; while not atEndOfFile; xxx2 書き込み(文字); 繰り返し; 繰り返し;
この構造は、 whileチェックを中間に置いたdoループと考えることができ、明確なループ 1.5ロジックが可能になります。さらに、個々のコンポーネントを省略することにより、この単一の構造は、ほとんどのプログラミング言語の複数の構造を置き換えることができます。xxx1を省略すると、テストが先頭にあるループ (従来のwhileループ) になります。xxx2を省略すると、テストが末尾にあるループになり、多くの言語のdo whileループに相当します。while を省略すると、無限ループになります。この構造では、極性を反転 ( notを追加)する必要がある早期終了とは異なり、途中でも条件の極性を同じに保つこともできます。[ 14 ] whileではなくuntilとして機能します。
この構造は広くサポートされておらず、ほとんどの言語では条件付き早期終了にif ... breakが代わりに使用されています。
これは、構文がBEGIN ... WHILE ... REPEATであるForthなどの一部の言語でサポートされています。[ 31 ]また、シェルスクリプト言語のBourne shell(sh)とbashでは、構文がwhile ... do ... doneまたはuntil ... do ... doneです。[ 32 ] [ 33 ]
while文 1 文 2 ... 条件 付き実行文 a 文 b ... 完了シェル構文が機能するのは、while(またはuntil)ループがコマンドのリストを条件として受け入れるためである。 [ 34 ]正式には次のようになる。
while test-commands ; do consequent-commands ; done
テスト コマンドのリストの値 (終了ステータス) は最後のコマンドの値であり、これらは改行で区切ることができ、上記の慣用的な形式になります。
C および C++ では、コンマ演算子を使用して同様の構造が可能です。また、他の言語でも同様の構造を使用して、ステートメントのリストを while 条件に押し込むことができます。
while (ステートメント1 、ステートメント2 、条件) {ステートメントa ;ステートメントb ; }これは合法ではあるが限界があり、主に短い「修正してからテストする」ケースでのみ使用される。例えば、次のようになる。[ 35 ]
while ( read_string ( s )、strlen ( s ) > 0 ) { // ... }ループバリアントとループ不変量はループの正しさを表現するために使用されます。[ 36 ]
実用的には、ループバリアントとは、初期値が非負の値である整数式です。このバリアントの値はループの各反復処理中に減少する必要がありますが、ループが正しく実行される間は決して負になってはなりません。ループバリアントは、ループが確実に終了することを保証するために使用されます。
ループ不変条件とは、ループの最初の反復処理の前に真であり、各反復処理の後も真であり続ける必要があるアサーションです。これは、ループが正しく終了すると、終了条件とループ不変条件の両方が満たされることを意味します。ループ不変条件は、連続する反復処理におけるループの特定の特性を監視するために使用されます。
Eiffelなどの一部のプログラミング言語では、ループバリアントと不変条件がネイティブサポートされています。一方、 Javaモデリング言語のループ文仕様のように、アドオンとしてサポートされる場合もあります。
一部のLisp方言は、ループを記述するための広範なサブ言語を提供しています。初期の例としては、InterlispのConversional Lispが挙げられます。Common Lisp [ 37 ]は、そのようなサブ言語を実装するLoopマクロを提供しています。
| プログラミング言語 | 条件付き | ループ | 早期退場 | ループ継続 | やり直す | リトライ | 正確性施設 | ||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 始める | 真ん中 | 終わり | 数値 | コレクション | 一般的な | 無限[1] | 変異体 | 不変 | |||||
| エイダ | はい | はい | はい | はい | 配列 | いいえ | はい | 深くネストされた | いいえ | ||||
| APL | はい | いいえ | はい | はい | はい | はい | はい | 深いネスト[3] | はい | いいえ | いいえ | ||
| C | はい | いいえ | はい | いいえ[2] | いいえ | はい | いいえ | 深いネスト[3] | 深いネスト[3] | いいえ | いいえ | いいえ | いいえ |
| C++ | はい | いいえ | はい | いいえ[2] | はい[9] | はい | いいえ | 深いネスト[3] | 深いネスト[3] | いいえ | いいえ | いいえ | いいえ |
| C# | はい | いいえ | はい | いいえ[2] | はい | はい | いいえ | 深いネスト[3] | 深いネスト[3] | いいえ | いいえ | いいえ | いいえ |
| コボル | はい | いいえ | はい | はい | いいえ | はい | いいえ | 深いネスト[15] | 深いネスト[14] | いいえ | |||
| コモンリスプ | はい | はい | はい | はい | 組み込みのみ[16] | はい | はい | 深くネストされた | いいえ | ||||
| D | はい | いいえ | はい | はい | はい | はい | はい[14] | 深くネストされた | 深くネストされた | いいえ | |||
| エッフェル | はい | いいえ | いいえ | はい[10] | はい | はい | いいえ | 1レベル[10] | いいえ | いいえ | いいえ[11] | 整数のみ[13] | はい |
| F# | はい | いいえ | いいえ | はい | はい | いいえ | いいえ | いいえ[6] | いいえ | いいえ | |||
| FORTRAN 77 | はい | いいえ | いいえ | はい | いいえ | いいえ | いいえ | 1レベル | はい | いいえ | いいえ | ||
| フォートラン90 | はい | いいえ | いいえ | はい | いいえ | いいえ | はい | 深くネストされた | 深くネストされた | いいえ | いいえ | ||
| Fortran 95以降 | はい | いいえ | いいえ | はい | 配列 | いいえ | はい | 深くネストされた | 深くネストされた | いいえ | いいえ | ||
| 行く | はい | いいえ | いいえ | はい | 組み込みのみ | はい | はい | 深くネストされた | 深くネストされた | いいえ | |||
| ハスケル | いいえ | いいえ | いいえ | いいえ | はい | いいえ | はい | いいえ[6] | いいえ | いいえ | |||
| ジャワ | はい | いいえ | はい | いいえ[2] | はい | はい | いいえ | 深くネストされた | 深くネストされた | いいえ | 非ネイティブ[12] | 非ネイティブ[12] | |
| JavaScript | はい | いいえ | はい | いいえ[2] | はい | はい | いいえ | 深くネストされた | 深くネストされた | いいえ | いいえ | いいえ | |
| コトリン | はい | いいえ | はい | 多分 | はい | いいえ | いいえ | 深くネストされた | 深くネストされた | いいえ | いいえ | いいえ | いいえ |
| 自然 | はい | はい | はい | はい | いいえ | はい | はい | はい | はい | はい | いいえ | ||
| OCaml | はい | いいえ | いいえ | はい | 配列、リスト | いいえ | いいえ | いいえ[6] | いいえ | いいえ | |||
| オーディン | いいえ[17] | いいえ | いいえ | いいえ[5] | 組み込みのみ | はい | いいえ[17] | 深くネストされた | 深くネストされた | ||||
| PHP | はい | いいえ | はい | いいえ[2] [5] | はい[4] | はい | いいえ | 深くネストされた | 深くネストされた | いいえ | いいえ | いいえ | いいえ |
| パール | はい | いいえ | はい | いいえ[2] [5] | はい | はい | いいえ | 深くネストされた | 深くネストされた | はい | |||
| パイソン | はい | いいえ | いいえ | いいえ[5] | はい | いいえ | いいえ | 深いネスト[6] | 深いネスト[6] | いいえ | いいえ | いいえ | いいえ |
| QB64 | はい | いいえ | はい | はい | いいえ | はい | はい | ループの種類ごとに1つのレベル | ループの種類ごとに1つのレベル | いいえ | いいえ | いいえ | いいえ |
| レボル | いいえ[7] | はい | はい | はい | はい | いいえ[8] | はい | 1レベル[6] | いいえ | いいえ | |||
| ルビー | はい | いいえ | はい | はい | はい | いいえ | はい | 深いネスト[6] | 深いネスト[6] | はい | はい | ||
| さび | はい | いいえ | いいえ | いいえ[5] | はい | いいえ | はい | 深くネストされた | 深くネストされた | いいえ | いいえ | いいえ | いいえ |
| 標準ML | はい | いいえ | いいえ | いいえ | 配列、リスト | いいえ | いいえ | いいえ[6] | いいえ | いいえ | |||
| 迅速 | はい | いいえ | はい | いいえ | はい | はい | いいえ | 深くネストされた | 深くネストされた | いいえ | いいえ | いいえ | いいえ |
| ビジュアルベーシック.NET | はい | いいえ | はい | はい | はい | いいえ | はい | ループの種類ごとに1つのレベル | ループの種類ごとに1つのレベル | いいえ | いいえ | いいえ | いいえ |
| パワーシェル | はい | いいえ | はい | いいえ[2] | はい | はい | いいえ | はい | はい | いいえ | いいえ | いいえ | いいえ |
| ジグ | はい | いいえ | いいえ | いいえ[5] | 組み込みのみ | いいえ | いいえ | 深くネストされた | 深くネストされた | いいえ | いいえ | いいえ | いいえ |
while (true)専用の言語構造ではないため、この目的では無限ループとしてカウントされません。for (init; test; increment)range()whileこの目的には関数を使用できる ため、特別な構成はありませんstd::for_eachテンプレートがあります。 [ 38 ]この機能は、これらのコンテナ上でマクロとして構築することもできます。 [ 39 ]retryで使用されます。GO TO。for多くのプログラミング言語、特により動的なプログラミングスタイルを好む言語では、非局所的な制御フローのための構造が提供されており、これにより実行は現在の実行ポイントから事前に宣言されたポイントへとジャンプします。注目すべき例を以下に示します。
初期のFortranIF ACCUMULATOR OVERFLOWコンパイラは、、、IF QUOTIENT OVERFLOWなどの例外条件を処理するための文をサポートしていましたIF DIVIDE CHECK。マシン独立性の観点から、これらの文はFORTRAN IVおよびFortran 66規格には含まれていませんでした。しかし、Fortran 2003以降では、モジュール内の関数呼び出しを通じて数値的な問題をテストすることが可能ですIEEE_EXCEPTIONS。
PL/I には、発生させたり、ON条件アクションによってインターセプトしたりできる、約 22 の標準条件 (例: ZERODIVIDE SUBSCRIPTRANGE ENDFILE) があります。プログラマーは、独自の名前付き条件を定義して使用することもできます。
非構造化 ifと同様に、指定できるステートメントは 1 つだけなので、多くの場合、制御フローを再開する場所を決定するために GOTO が必要になります。
残念ながら、一部の実装では空間と時間の両方で大きなオーバーヘッドが発生したため (特に SUBSCRIPTRANGE)、多くのプログラマーは条件の使用を避けようとしました。
構文の典型的な例:
ON条件GOTOラベル
多くの現代言語は、例外処理をネイティブにサポートしています。一般的に、例外制御フローは、例外オブジェクトがスロー(発生)される(つまり、例外が生成される)ことから始まります。その後、制御はコールスタックの最も内側の例外ハンドラに進みます。ハンドラが例外を処理すると、フロー制御は通常の状態に戻ります。それ以外の場合は、制御は外側のハンドラへと進み、いずれかのハンドラが例外を処理するか、プログラムが最も外側のスコープに到達して終了するまで続きます。制御が徐々に外側のハンドラへと流れるにつれて、コールスタックのポップなど、通常発生するような処理は自動的に行われます。
次のC++コードは、構造化例外処理の例を示しています。 の実行から例外が伝播しdoSomething()、例外オブジェクトの型がcatch節で指定された型のいずれかと一致する場合、その節が実行されます。例えば、SomeExceptionによって 型の例外が伝播された場合doSomething()、制御は2行目から4行目に移動し、「Caught SomeException」というメッセージが出力された後、制御は tryステートメントの後の8行目に移動します。その他の型の例外が伝播された場合、制御は2行目から6行目に移動します。例外が発生しなかった場合、制御は2行目から8行目に移動します。
試す{何かを行う();} catch ( const SomeException & e )std :: println ( "SomeException をキャッチしました: {}" , e . what ());}キャッチ(...) {std :: println ( "不明なエラー" );}次のステップを実行します。多くの言語ではC++のキーワード(throw、 、try)が使われますcatchが、言語によっては他のキーワードが使われます。例えば、Adaではexception例外ハンドラを導入するためwhenに ではなく とが使われますcatch。AppleScriptでは、次のAppleScriptコードに示すように、構文にプレースホルダが組み込まれ、例外に関する情報が抽出されます。
try myNumber をmyNumber / 0に設定し、エラーの場合はe の数値nをfからtまでの部分的な結果を返します。e が「ゼロで割ることはできません」の場合、ダイアログ「その操作は実行しないでください」を表示します。end try多くの言語(Object Pascal、D、Java、C#、Pythonなど)では、finally文末の節はtry、例外が文の残りの部分から伝播するtryかどうかに関係なく、try文の最後に実行されます。次のC#コードは、ストリームstreamが閉じられていることを保証します。
FileStreamストリーム= null ; try {ストリーム= new FileStream ( "logfile.txt" 、FileMode . Create ); return ProcessStuff (ストリーム); }最後に{ストリーム!= null の場合{ストリーム. Close ( ) ; } }このパターンは一般的であるため、C#ではusingクリーンアップを確実に行うためのステートメントが用意されています。次のコードでは、ProcessStuff() が例外を伝播した場合でも、streamオブジェクトは解放されます。PythonのwithステートメントとRubyのブロック引数もFile.open同様の効果を得るために使用されています。
FileStreamストリームを使用して( " logfile.txt " 、FileMode.Create ) ) { return ProcessStuff ( stream ) ; }コンピュータサイエンスにおいて、継続とはコンピュータプログラムの制御状態を抽象的に表現したものです。継続はプログラムの制御状態を実装(具体化)します。つまり、継続はプロセス実行の特定の時点における計算プロセスを表すデータ構造です。作成されたデータ構造は、実行環境に隠されるのではなく、プログラミング言語からアクセスできます。継続は、例外、ジェネレータ、コルーチンなど、 プログラミング言語の他の制御メカニズムをエンコードするのに役立ちます。
「現在の継続」または「計算ステップの継続」とは、実行コードの観点から見ると、プログラム実行の現在の時点から派生する継続です。継続という用語は、第一級継続を指す場合もあります。第一級継続とは、プログラミング言語に任意の時点での実行状態を保存し、プログラムの後の時点でその時点に複数回戻る機能を提供する構造です。
コンピュータサイエンスにおいて、ジェネレータとはループの反復動作を制御するために使用できるルーチンのことである。すべてのジェネレータはイテレータでもある。[ 40 ]ジェネレータは、パラメータを持ち、呼び出し可能で、値のシーケンスを生成するという点で、配列を返す関数と非常によく似ている。しかし、すべての値を含む配列を構築してそれらを一度にすべて返すのではなく、ジェネレータは値を1つずつ生成するため、必要なメモリが少なくて済み、呼び出し元は最初のいくつかの値の処理をすぐに開始できる。つまり、ジェネレータは関数のように見えるが、イテレータのように動作する。
ジェネレータは、コルーチンや第一級継続などの、より表現力豊かな制御フロー構造によって実装することができます。[ 41 ]ジェネレータはセミコルーチンとも呼ばれ、[ 42 ]コルーチンの特殊なケース(より弱いケース)であり、ジャンプ先のコルーチンを指定するのではなく、常に呼び出し元に制御を返します(値を返す場合)。コルーチンとジェネレータの比較を参照してください。
コルーチンは、実行を中断および再開できるコンピュータプログラムコンポーネントであり、協調型マルチタスク用のサブルーチンを一般化します。コルーチンは、協調タスク、例外、イベントループ、反復子、無限リスト、パイプといった一般的なプログラムコンポーネントの実装に適しています。
これらは「実行を一時停止できる関数」と説明されています。[ 43 ]
メルビン・コンウェイは1958年にアセンブリプログラムの構築にコルーチンという用語を適用した際にこの用語を作り出した。[ 44 ]コルーチンの最初の解説は1963年に出版された。[ 45 ]
コンピュータプログラミングにおいて、COMEFROM は制御フロー文であり、制御が COMEFROM 引数で指定されたポイントに到達した際に、制御フローを次の文にジャンプさせます。この文はgotoの逆の動作を意図しており、真剣なコンピュータサイエンスというよりはむしろジョークとして扱われています。指定されたジャンプポイントはラベルで識別されることが多いです。例えば、 はCOMEFROM x制御がラベル に到達した際にx、COMEFROM の次の文に制御が続くことを指定します。
goto との大きな違いは、goto がコードのローカル構造に依存するのに対し、COMEFROM はグローバル構造に依存することです。goto 文は制御がその文に到達した時点で制御を移しますが、COMEFROM ではプロセッサ(つまりインタープリタ)が COMEFROM 文をスキャンし、指定されたポイントのいずれかに制御が到達した時点でプロセッサがジャンプできるようにします。ジャンプポイント付近では制御が実際にジャンプするかどうかが示されないため、結果として得られるロジックは理解しにくい傾向があります。COMEFROM 文がそのポイントを参照しているかどうかを確認するには、プログラム全体を調査する必要があります。
COMEFROM文のセマンティクスはプログラミング言語によって異なります。言語によっては、指定されたポイントの文が実行される前にジャンプが発生しますが、他の言語では、指定されたポイントの文が実行された後にジャンプが発生します。言語によっては、同じポイントを参照する複数のCOMEFROM文が無効、非決定的、特定の順序で実行される、あるいはスレッド化されたインターカルで見られるように並列処理やその他の同時処理を引き起こす場合があります。
COMEFROMは当初、ジョークアセンブリ言語命令のリスト('CMFRM')で見られました。1973年にR. Lawrence ClarkがDatamationの記事[ 46 ]で詳しく説明しました。これは、 Edsger Dijkstraの手紙「Go To文は有害であると考えられる」への返答として書かれたものです。COMEFROMは最終的に、難解なプログラミング言語INTERCALのC-INTERCAL版に、さらに難解な「計算型COMEFROM」とともに実装されました。Fortranでは、「代入型COME FROM」と「DONT」文(既存の「DO」ループを補完する)の提案もありました[ 47 ]。
Zahnの構成概念は1974年に提案され[ 48 ] 、 Knuth (1974)で議論された。ここでは修正版を示す。
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 ("アイテムがテーブル内にありません"); 終了; go to