制御フロー

ソフトウェアにおいて制御フロー(または制御のフロー)は、実行が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

条件分岐

条件文は、ブール式の値に基づいて制御を切り替えます。一般的なバリエーションには以下が含まれます

if-goto
条件に基づいてラベルにジャンプします。これは、類似の機械語命令を厳密に模倣した高水準プログラミング文です
if-then
ジャンプに限定されるのではなく、式が真の場合に文またはブロックが実行されます。キーワードを含まない言語ではthen、これはif文と呼ばれることがあります
if-then-else
if-thenと似ていますが、条件が偽の場合に実行される2番目のアクションがあります。キーワードを含まない言語ではthen、これはif-else文と呼ばれます
ネスト
条件文は、他の条件文の中にネストされることがよくあります
算術if文
初期のFortranには、数値が負、ゼロ、正のいずれであるかをテストする算術if文(別名、3元if文)がありました。この文はFortran-90では廃止とみなされ、Fortran 2018では削除されました
関数型
一部の言語には関数型があります。例えば、Lispの言語などです cond
演算子
一部の言語には三項条件演算子などの演算子形式があります
および がない限り
PerlはCスタイルifを および でwhen補完しunlessます
メッセージ
Smalltalkは、言語構造ではなく、条件文を実装するためにメッセージ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" fi          

Pythonの場合:

 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 ;; esac               

Lispの場合:

( 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]

HaskellSchemeなどの関数型プログラミング言語では、再帰プロセス反復プロセスの両方が、構文上のループ構造ではなく末尾再帰プロシージャで表現されます。

数値

比較的シンプルでありながら便利なループは、数値の範囲を反復処理します。単純な形式では、ある整数値から始まり、それより大きな整数値で終わり、その間の各整数値で反復処理を行います。多くの場合、増分は任意の整数値にすることができ、負の値にしてより大きな値からより小さな値へとループすることもできます

BASICの例:

FOR I = 1 TO N xxx NEXT I       

Pascalの例:

for I := 1 to N do 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番目の部分である条件は各ループの前に評価されるため、ループは事前テストとなります。1番目の部分は状態を初期化する場所で、3番目の部分は次の反復処理のためのインクリメントですが、どちらの側面も他の場所で実行できます。次のCコードは、iを0からn-1まで反復する数値ループのロジックを実装しています。

for ( int i = 0 ; i < n ; ++ i ) { xxx }          

列挙

一部のループ構造はコレクションの項目を列挙し、各項目を反復します。

Smalltalkの例:

someCollection  do : [ : eachElement  | xxx ]

Pascalの例:

コレクション内のアイテムに対して、 begin xxx end ;を実行します       

Rakuの例:

foreach ( item ; myCollection ) { xxx }

TCLの例:

foreach  someArray { xxx }   

PHPの例:

foreach  ( $someArray  as  $k  =>  $v )  {  xxx  }

Javaの例:

Collection <String> coll ; for ( String s : coll ) { }       

C#の例:

foreach ( myStringCollection内の文字列s ) { xxx }       

PowerShell で 'foreach' が 'ForEach-Object' のエイリアスである例:

いくつかのコレクション |  foreach  {  $_  }

Fortranの例:

forall ( index = first : last : step ... )     

Scalaには、コレクション制御ループを一般化するfor式があり、非同期プログラミングなどの他の用途もサポートしています。Haskellはdo式と内包表記があり、これらを組み合わせることでScalaのfor式と同様の機能を提供します。

無限

コンピュータプログラミングにおいて無限ループ(または無限ループ)[9] [10]とは、スイッチによる電源のオフやプラグの抜き差しなど、外部からの介入がない限り、記述されたとおりに無限に続く命令のシーケンスです。意図的な場合もあります

コンピュータ プログラムに無限ループが含まれているかどうかを判断する一般的なアルゴリズムは存在しません。これが停止問題です。

ループ1.5問題

一般的なループ構造では、文の繰り返しまたは条件の繰り返しにより、コードの重複が発生することがあります。これは様々な理由で発生し、コードの重複を排除または最小限に抑えるための様々な解決策が提案されています。[11] goto文という従来の非構造化解決策以外に[12]一般的な構造化解決策としては、ループ内に条件文(if文)を置く(条件は重複する可能性があるが文は重複しない)か、繰り返しロジックを関数でラップする(関数呼び出しは重複するが文は重複しない)ことが挙げられます。[11]

よくあるケースとしては、ループの開始は常に実行されますが、最後の反復で終了がスキップされる場合があります。[12]これは、ダイクストラによって「 n回半」実行されるループと名付けられ、 [13]現在ではループ半問題と呼ばれています。[8]よくあるケースとしては、最初の部分でデータを読み取り、データの終了をチェックし、次に 2 番目の部分でデータを処理する場合や、処理を行い、終了をチェックし、次の反復の準備を行う場合が挙げられます。[12] [8]これらの場合、ループの最初の部分は回実行されますが、2 番目の部分は回しか実行されません。

この問題は少なくとも1967年からクヌースによって認識されており、ヴィルトはループの早期終了による解決法を提案した。[14] 1990年代以降、 breakを使った解決策が最も一般的に教えられてきた。 [8]

ループ   条件 分岐 文、繰り返し

この解決策の微妙な点は、条件が通常のwhile文の条件とは逆になっていることです。while文条件... repeatを途中の exit で書き直すには、条件を逆にする必要があります。つまり、 loop ... if not condition exit ... repeat です。中間の制御構造に test があるループは、条件を逆にすることなく、明示的に半分のループのユースケースをサポートしています。[14]

非構造化

ループ構造は、ループ文の後に別の反復処理を実行するか、実行を継続するかを決定する構造化された完了基準を提供します。しかし、多くの言語では、様々な非構造化制御フロー構造がサポートされています。

次の反復の早期実行
一部の言語では、次の反復のためにループ本体の先頭に制御をジャンプする構造を提供しています。例えば、continue(最も一般的)、、skip[ 15] cycle(Fortran)、next(PerlとRuby)などです
反復をやり直す
Perl [16]やRuby [17]などの言語には、redo同じ繰り返しの本体の先頭にジャンプする文があります
再開
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 read(char); whileテスト; while  not atEndOfFile; xxx2 書き込み(文字); 繰り返し; 繰り返し;

この構造は、 whileチェックを中間に置いたdoループと考えることができ、明確なループ 1.5 ロジックが可能になります。さらに、個々のコンポーネントを省略することにより、この単一の構造は、ほとんどのプログラミング言語の複数の構造を置き換えることができます。xxx1省略すると、テストが先頭にあるループ (従来のwhileループ) になります。xxx2省略すると、テストが末尾にあるループになり、多くの言語のdo whileループに相当します。while を省略すると、無限ループになります。この構造では、極性を反転 ( notを追加)する必要がある早期終了とは異なり、途中でも条件の極性を同じに保つこともできます。[14] whileではなくuntilとして機能します

この構造は広くサポートされておらず、ほとんどの言語では条件付き早期終了にif ... breakが代わりに使用されています。

これは、 Forthなどの一部の言語でサポートされており、その構文はBEGIN ... WHILE ... REPEATです。[31]また、シェルスクリプト言語のBourne shellsh)と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]いいえいいえいいえいいえ
COBOLはいいいえはいはいいいえはいいいえ深いネスト[15]深いネスト[14]いいえ
Common Lispはいはいはいはい組み込みのみ[16]はいはい深いネストいいえ
Dはいいいえはいはいはいはいはい[14]深いネスト深いネストいいえ
エッフェルはいいいえいいえはい[10]はいはいいいえ1階[10]いいえいいえいいえ[11]整数のみ[13]はい
F#はいいいえいいえはいはいいいえいいえいいえ[6]いいえいいえ
FORTRAN 77はいいいえいいえはいいいえいいえいいえ1レベルはいいいえいいえ
Fortran 90はいいいえいいえはいいいえいいえはい深いネスト深いネストいいえいいえ
Fortran 95以降はいいいえいいえはい配列いいえはい深いネスト深いネストいいえいいえ
Goはいいいえいいえはい組み込みのみはいはい深いネスト深いネストいいえ
Haskellいいえいいえいいえいいえはいいいえはいいいえ[6]いいえいいえ
Javaはいいいえはいいいえ[2]はいはいいいえ深いネスト深いネストいいえ非ネイティブ[12]非ネイティブ[12]
JavaScriptはいいいえはいいいえ[2]はいはいいいえ深いネスト深いネストいいえいいえいいえ
Kotlinはいいいえはいたぶんはいいいえいいえ深いネスト深いネストいいえいいえいいえいいえ
ナチュラルはいはいはいはいいいえはいはいはいはいはいいいえ
OCamlはいいいえいいえはい配列、リストいいえいいえいいえ[6]いいえいいえ
オーディンいいえ[17]いいえいいえいいえ[5]組み込みのみはいいいえ[17]深いネスト深いネスト
PHPはいいいえはいいいえ[2] [5] はい[4]はいいいえ深いネスト深いネストいいえいいえいいえいいえ
Perlはいいいえはいいいえ[2] [5] はいはいいいえ深いネスト深いネストはい
Pythonはいいいえいいえいいえ[5]はいいいえいいえ深いネスト[6]深いネスト[6]いいえいいえいいえいいえ
Rebolいいえ[7]はいはいはいはいいいえ[8]はい1レベル[6]いいえいいえ
Rubyはいいいえはいはいはいいいえはい深いネスト[6]深いネスト[6]はいはい
Rustはいいいえいいえいいえ[5]はいいいえはい深いネスト深いネストいいえいいえいいえいいえ
標準MLはいいいえいいえいいえ配列、リストいいえいいえいいえ[6]いいえいいえ
Swiftはいいいえはいいいえはいはいいいえ深いネスト深いネストいいえいいえいいえいいえ
Visual Basic .NETはいいいえはいはいはいいいえはいループの種類ごとに1つのレベルループの種類ごとに1つのレベルいいえいいえいいえいいえ
PowerShellはいいいえはいいいえ[2]はいはいいいえはいはいいいえいいえいいえいいえ
Zigはいいいえいいえいいえ[5]組み込みのみいいえいいえ深いネスト深いネストいいえいいえいいえいいえ
  1. a while (true)は専用の言語構造ではないため、この目的では無限ループとはみなされません
  2. a b c d e f g h C のループは一般的なループ構造であり、特にカウント用のループではありませんが、カウント用によく使用されます。 for (init; test; increment)
  3. a b c APL、C、C++、C# では、ラベルと goto を使用してディープ ブレークを実現できます。
  4. オブジェクトの反復処理は PHP 5 で追加されました
  5. a b c d e f カウント ループは、増分リストまたはジェネレータ (たとえば、Python の) を反復処理することによってシミュレートできます range()
  6. a b c d e 例外処理を使用することで、ディープ ブレークを実現できます。
  7. awhileこの目的には関数を使用できる ため、特別な構成はありません。
  8. a 特別な構造はありませんが、ユーザーは一般的なループ関数を定義できます。
  9. C ++11標準では、範囲ベースの for が導入されました 。STL STLコンテナを反復処理し、各要素に対して単項関数を呼び出すことができるstd::for_each テンプレート関数があります[38]この機能は、これらのコンテナ上でマクロとして構築することもできます[39]
  10. 数値 ループは整数間隔にわたる反復によって実行され、終了のための追加条件を含めることで早期に終了します。
  11. Eiffel は予約語をサポートしていますが、ループ制御ではなく例外処理retryで使用されます
  12. a Java モデリング言語(JML) 動作インターフェース仕様言語が 必要です。
  13. a ループバリアントは整数である必要があります。超限バリアントはサポートされていません。Eiffel: ループバリアントが整数である理由
  14. Dは 無限コレクションをサポートし、それらのコレクションを反復処理する機能を備えています。特別な構文は必要ありません。
  15. および手順を使用することで、深い ブレークを実現できますGO TO
  16. Common Lisp はジェネリックコレクション型の概念より古くから存在します。
  17. a b Odin の一般的なループは、条件付きループと無限ループの構文ショートカットをサポートしています。for

非局所的

多くのプログラミング言語、特により動的なプログラミングスタイルを好む言語では、現在の実行ポイントから事前に宣言されたポイントへ実行をジャンプさせる非局所的な制御フローの構成要素を提供しています。注目すべき例を以下に示します

条件処理

初期のFortranIF ACCUMULATOR OVERFLOWコンパイラは、、、IF QUOTIENT OVERFLOWなどの例外条件を処理するための文をサポートしていました。マシン独立性を保つため、これらはFORTRAN IVおよびFortran 66標準には含まれていませんでした。しかし、Fortran 2003以降では、モジュールIF DIVIDE CHECK内の関数呼び出しを介して数値の問題をテストすることが可能ですIEEE_EXCEPTIONS

PL/I には、発生させたり、ON条件アクションによってインターセプトしたりできる、約 22 の標準条件 (例: ZERODIVIDE SUBSCRIPTRANGE ENDFILE) があります。プログラマーは、独自の名前付き条件を定義して使用することもできます。

非構造化 ifと同様に、指定できるステートメントは 1 つだけなので、多くの場合、制御フローを再開する場所を決定するために GOTO が必要になります。

残念ながら、一部の実装では空間と時間の両方で大きなオーバーヘッドが発生したため (特に SUBSCRIPTRANGE)、多くのプログラマーは条件の使用を避けようとしました。

構文の典型的な例:

ON 条件 GOTO ラベル

例外処理

多くの現代言語は、構造化された例外処理構造をサポートしています。これは、ジャンプセマンティクス(goto)に依存しません。一般的に、例外制御フローは、例外オブジェクトがスローされる(つまり、発生する)ことから始まります。次に、制御はコールスタックの最も内側の例外ハンドラに進みます。ハンドラが例外を処理する場合、フロー制御は通常に戻ります。それ以外の場合、制御は、例外を処理するか、プログラムが最も外側のスコープに到達して終了するまで、外側のハンドラに進みます。制御が徐々に外側のハンドラに流れるにつれて、コールスタックのポップなど、通常発生する側面は自動的に処理されます

次のC++コードは、構造化例外処理の例を示しています。 の実行から例外が伝播しdoSomething()、例外オブジェクトの型がcatch節で指定された型のいずれかと一致する場合、その節が実行されます。例えば、SomeExceptionによって 型の例外が伝播された場合doSomething()、制御は2行目から4行目に移動し、「Caught SomeException」というメッセージが出力された後、制御は tryステートメントの後の8行目に移動します。その他の型の例外が伝播された場合、制御は2行目から6行目に移動します。例外が発生しなかった場合、制御は2行目から8行目に移動します。

試してください{  doSomething ();} catch ( const SomeException & e )     std :: println ( "SomeException をキャッチしました: {}" , e . what ()); }キャッチ(...) {    std :: println ( "不明なエラー" );}doNextThing ();

多くの言語では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文末の節はtrytry文の末尾で実行されます。これは、例外が文の残りの部分に伝播する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 ("アイテムはテーブル内にありません"); 終了

参照

参考文献

  1. ^ マティアス・ペイヤー、ヴォロディミル・クズネツォフ「CFI、CPS、CPIの特性の違いについて」nebelwelt.net2016年6月1日閲覧
  2. ^ 「Adobe Flashのバグ発見により新たな攻撃緩和策が生まれる」Dark Reading、2015年11月10日。 2016年6月1日閲覧
  3. ^ Endgame. 「Endgame、Black Hat USA 2016に登壇」www.prnewswire.com (プレスリリース) . 2016年6月1日閲覧
  4. ^ 「C言語のネストされたループとその例」GeeksforGeeks . 2019年11月25日. 2024年3月14日閲覧
  5. ^ 「Pythonのネストされたループ」www.w3schools.com . 2024年3月14日閲覧
  6. ^ Dean, Jenna (2019年11月22日). 「ネストされたループ」. The Startup . 2024年3月14日閲覧。
  7. ^ Knuth, Donald E. (1974). 「ステートメントを用いた構造化プログラミング」. Computing Surveys 6 (4): 261– 301. CiteSeerX 10.1.1.103.6084 . doi :10.1145/356635.356640. S2CID  207630080.go to 
  8. ^ abcde Roberts, E. [1995]「ループ終了と構造化プログラミング:議論の再開」、Wayback Machineで2014年7月25日にアーカイブ、ACM SIGCSE Bulletin、(27)1:268–272。
  9. ^ 「Endless loop dictionary definition」. 2020年8月1日時点のオリジナルよりアーカイブ2020年1月22日閲覧。
  10. ^ “無限ループ(エンドレスループ)とは”. 2019年7月15日時点のオリジナルよりアーカイブ2020年1月22日閲覧。
  11. ^ ab 「Messy Loop Conditions」WikiWikiWeb 2014年11月3日。
  12. ^ abc Knuth 1974、p. 278、単純な反復。
  13. ^ エドガー・W・ダイクストラ、ドナルド・クヌースへの1974年1月3日の個人的な通信、クヌース(1974年、p.278、Simple Iterations)に引用
  14. ^ abc Knuth 1974、279ページ。
  15. ^ 「ループとは何か、そしてどのように使えるのか?」。2020年7月28日時点のオリジナルよりアーカイブ2020年5月25日閲覧。
  16. ^ "redo - perldoc.perl.org". perldoc.perl.org . 2020年9月25日閲覧
  17. ^ "control_expressions - Ruby 2.4.0のドキュメント". docs.ruby-lang.org . 2020年9月25日閲覧
  18. ^ "control_expressions - Ruby 2.3.0のドキュメント". docs.ruby-lang.org . 2020年9月25日閲覧
  19. ^ ループ半問題を解決する一般的な方法です。
  20. ^ 上級Bashスクリプトガイド: 11.3. ループ制御
  21. ^ PHPマニュアル:「break」
  22. ^ perldoc: 最後
  23. ^ comp.lang.c FAQ リスト · 「質問 20.20b」
  24. ^ "named-loops". open-std.org . 2024年9月18日.
  25. ^ 「情報技術 — プログラミング言語 — C」(PDF) . open-std.org . 2025年5月4日.
  26. ^ [Python-3000] PEP 3136 の発表、Guido van Rossum
  27. ^ ab Kozen, Dexter (2008). 「ボーム・ヤコピニの定理は命題的に偽である」. プログラム構築数学(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
  28. ^ 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 (1974) によって引用されている
  29. ^ David Anthony Watt、William Findlay (2004). 『プログラミング言語設計コンセプト』 John Wiley & Sons. pp.  215– 221. ISBN 978-0-470-85320-7
  30. ^ Dahl、Dijkstra、Hoare、「構造化プログラミング」Academic Press、1972年
  31. ^ 「6. ループさせてみよう」。
  32. ^ 「3.2.5.1 ループ構造」、GNU Bashリファレンスマニュアル、2025年5月18日
  33. ^ 「言語はどうすればループ1.5のエラー発生率を低くできるだろうか?」Stack Exchange: プログラミング言語の設計と実装
  34. ^ 「3.2.4 コマンドリスト」、GNU Bashリファレンスマニュアル、2025年5月18日
  35. ^ 「コンマ演算子 , は何をするのでしょうか?」。Stack Overflow
  36. ^ マイヤー、ベルトラン(1991年)『エッフェル:言語』プレンティス・ホール、  pp.129-131
  37. ^ 「Common Lisp LOOP マクロ」。
  38. ^ for_each. Sgi.com. 2010年11月9日閲覧。
  39. ^ 第1章 Boost.Foreach 2010年1月29日アーカイブ、Wayback Machineより。Boost-sandbox.sourceforge.net (2009年12月19日)。2010年11月9日閲覧。
  40. ^ イテレータとジェネレータの違いは何ですか?
  41. ^ Kiselyov, Oleg (2004年1月). 「Schemeでコレクションを走査する一般的な方法」
  42. ^ アンソニー・ラルストン (2000). コンピュータサイエンス百科事典. Nature Pub. Group. ISBN 978-1-56159-248-720135月11日閲覧
  43. ^ 「Python 3.5でasync/awaitは一体どうやって動くんだ?」Tall, Snarky Canadian . 2016年2月11日. 2023年1月10日時点のオリジナルよりアーカイブ2023年1月10日閲覧。
  44. ^ Knuth, Donald Ervin (1997). 基礎アルゴリズム(PDF) . 『コンピュータプログラミングの芸術』第1巻(第3版). Addison-Wesley. セクション1.4.5: 歴史と参考文献, pp. 229. ISBN 978-0-201-89683-12019年10月21日時点のオリジナルからアーカイブ(PDF)
  45. ^ Conway, Melvin E. (1963年7月). 「分離可能な遷移図コンパイラの設計」(PDF) . Communications of the ACM . 6 (7). ACM: 396– 408. doi :10.1145/366663.366704. ISSN  0001-0782. S2CID 10559786. 2022年4月6日時点のオリジナルより アーカイブ(PDF) . 2019年10月21日閲覧– ACM Digital Libraryより.
  46. ^ クラーク、ローレンス、「どこから来たのかがわからなければ、どこにGOTOすればいいのかわからない。この言語的革新はすべての期待に応えるものだ」、Datamation(記事)、2018年7月16日時点のオリジナルからアーカイブ、 2004年9月24日閲覧
  47. ^ Modell, Howard; Slater, William (1978年4月). 「構造化プログラミングは有害であると考えられる」. ACM SIGPLAN Notices . 13 (4): 76– 79. doi : 10.1145/953411.953418 . 2014年7月18閲覧
  48. ^ Zahn, CT「自然なトップダウン構造化プログラミングのための制御ステートメント」、1974 年にパリで開催されたプログラミング言語シンポジウムで発表。

さらに詳しい情報

  • Hoare, CAR「Partition: Algorithm 63」、「Quicksort: Algorithm 64」、「Find: Algorithm 65」。Comm. ACM 4, 321–322, 1961
  • ウィキメディア・コモンズにおける制御フローに関連するメディア
  • Go To文は有害とみなされる
  • GOTOレスプログラミングの言語的貢献
  • 「Go To ステートメントを使用した構造化プログラミング」(PDF)。2009年8月24日にオリジナル(PDF)からアーカイブ。 (2.88 MB)
  • 「IBM 704 マニュアル」(PDF)   (31.4 MB)
Retrieved from "https://en.wikipedia.org/w/index.php?title=Control_flow&oldid=1320701506#continue-statement"