削除された内容 追加された内容
Melan (会話 | 投稿記録)
福いたら里 (会話) による ID:47744775 の版を取り消し
m cw056:アスキーアートの矢印
 
(26人の利用者による、間の49版が非表示)
1行目:
{{出典の明記|date=2025年9月}}
'''制御構造'''(せいぎょこうぞう)あるいは'''制御フロー'''とは、[[計算機科学]]において[[文 (プログラミング)|文]]や[[命令 (コンピュータ)|命令]]、また[[命令型プログラミング]]や[[宣言型プログラミング]]における[[サブルーチン]]呼び出しを実行または評価する順序を意味する。
'''制御構造'''(せいぎょこうぞう)は、[[コンピュータ]]・[[プログラミング言語]]、特に[[手続き型プログラミング]]<ref>procedural programming</ref>や[[命令型プログラミング]]<ref>imperative programming</ref>において、ループや飛び越しなどといった、手続き([[プロシージャ]])中の実行順を順次実行から変化させたり、[[サブルーチン]]呼出しやその戻り、などといった制御を行う「[[文 (プログラミング)|文]] 」などの構造(言語の構成要素)である<ref>{{Cite book|和書
| author= bit 編集部
|title= bit 単語帳
| year=1990
| date=1990-8-15
|page=122
|publisher=[[共立出版]]|isbn =4-320-02526-1 }}</ref>。
 
制御構造の種類は言語によって様々だが、典型的には以下のようなものがある(用語「ブロック」については、[[ブロック (プログラミング)|ブロック]] の記事を参照)。
命令型[[プログラミング言語]]では、'''制御構文'''(せいぎょこうぶん)とは命令の実行順序を通常の逐次実行以外の順番に変化させる構文であり、2つ以上の経路のいずれかを選択するものである。正格でない関数型言語では、関数や言語構成物が同様の結果を達成するために存在しているが、それらは必ずしも制御構文とは呼ばれない。
* 無条件に実行箇所を移動する(無条件の[[分岐命令]]、ジャンプ)
* 何らかの条件の成立・不成立に従い、ブロックの実行・不実行を選択する(条件付き分岐命令、選択)
* ブロックを繰り返し実行する([[ループ (プログラミング)|ループ]])
* ジャンプの一種だが、その続きに戻れるもの([[サブルーチン]]呼出、[[コルーチン]])
* [[継続]](特にcall/cc)
* プログラムの停止(理論的には重要だが([[停止性問題]]を参照)、実際的にはexitシステムコールなど、OSのプロセス制御機構を使うことが専らであり、言語機能として制御構造で持つ意味は無い)
[[割り込み (コンピュータ)|割り込み]]と[[シグナル (Unix)|シグナル]]は制御フローを変化させる別の機構であり、サブルーチンに似ているが、通常は言語内からではなく外部のイベントなどの結果として非同期に発生するものである(言語内から外部のイベントを引き起こし、結果として発生させることもできる例もある。たとえば、[[ゼロ除算]]などでそのようになるシステムがある)。[[自己書き換えコード]]も[[副作用 (プログラム)|副作用]]によって制御フローを変化させることができる。割り込み的なものを扱うことができるプログラミング言語(の機能)はいくつかある。自己書き換えをプログラマが明示的に扱えるプログラミング言語はあまりないが、初期化の時だけ特別扱いが必要といったコードの最適化に自己書き換えを利用する、処理系の実装上のテクニックといったようなものもある。
{{seealso|分岐命令}}
[[機械語]]において制御構造に相当するのは[[分岐命令]]で、通常は連続的にカウントが進められる[[プログラムカウンタ]]を、不連続に変更する命令である。ほぼ全ての[[プロセッサ]]は(条件付きおよび無条件の)[[分岐命令]]を持つ(無いと、理論的に言うと[[計算可能性理論|計算可能性]]を満たさないため、コンピュータとして成立しない)。また、サブルーチン呼出しを(大なり小なりハードウェアによる支援を含んで)サポートする命令を持つプロセッサが多い(こんにち存在する汎用プロセッサの著名なISAでこのサポートの無いのは、60年代の設計であるIBMの[[System/360]]ぐらいであろう)。全くハードウェアによるサポートが無いと、サブルーチン呼出しに面倒なトリックが必要なことがある。一方で前述のような、ループの処理(レジスタの値をデクリメントし、ゼロでなければジャンプ、といったような)を直接サポートするような命令を持つプロセッサは、専用命令として積極的に持つものもあるが、一般にはあまり多くなく、特にいわゆる[[RISC]]では避けられる。そのため、コンパイラのコード生成は制御構造からジャンプ命令等を適宜組み合わせたコードを生成するように実装される。
 
== 原始的な機能 ==
制御構文の種類は言語によって様々だが、大まかに以下のように分類できる。
 
* 別の文から実行を継続する([[分岐命令]]、ジャンプ)
* 何らかの条件が成立したときだけ文の並びを実行する(選択、[[条件分岐]])
* 文の並びを繰り返し実行する([[ループ (プログラミング)|ループ]]、コードの先頭に近いほうへの条件分岐と等価)
* 離れた箇所の文の並びを実行し、元の場所に制御を戻す([[サブルーチン]]、[[コルーチン]]、[[継続]])
* プログラムを停止し、その後の実行を防ぐ(停止)
 
[[割り込み (コンピュータ)|割り込み]]と[[シグナル (ソフトウェア)|シグナル]]は制御フローを変化させる別の機構であり、サブルーチンに似ているが、言語内の制御構文ではなく外部のイベントなどの結果として非同期に発生するものである。[[自己書き換えコード]]も[[副作用 (プログラム)|副作用]]によって制御フローを変化させることができる。ただし、それを制御構文として明確化することはほとんどない。
 
[[機械語]]や[[アセンブリ言語]]では、制御命令は[[プログラムカウンタ]]を変更する働きを持つ。多くの[[CPU]]では制御命令として(条件付きおよび無条件の)[[分岐命令]]しか用意されていない。
 
== 基本構文 ==
=== ラベル ===
{{Main|ラベル (プログラミング)}}
ラベルとは、[[ソースコード]]上の固定の位置を示す何らかの名前(あるいは数値)シンボルであり、ソースコード上goto他の箇所の制御構文飛び先や、break抜ける対象として参照される。ソースコード上GCC拡張であるが、void * ポインタ位置を記録す値として扱うこともでき以外の効果はない(Labels as Values<ref>https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html</ref>)
 
[[行番号]]は一部の言語([[FORTRAN]]や[[BASIC]])でラベルの一種として使われ、負でない整数がソースコードの各テキスト行の先頭に置かれる。行番号を使用する言語では、連続で実行される文には行番号が増えるように行番号を与える必要がある。ただ、とBASICか知らない者は誤解しているが連続FORTRANにはそのよう制限は無い。BASICで行番号が昇順なのは、テキストの編集にフルスクリーンエディタが一般的ではなかった時代のラインエディタのみによる編集では、行番号に従ってシステムが並べ直してくれたほうが便利だったからであり、「行番号が増えるように行番号を与える必要はながある」とうのは本末逆転である。例えば BASIC では次のようになっている。
 
<sourcesyntaxhighlight lang="qbasic">
10 LET X = 3
20 PRINT X
</syntaxhighlight>
</source>
 
[[C言語|C]]や[[Ada]]といった言語のラベルは[[識別子]]であり、文の前に書かれ、その直後にコロンが書かれる。例えば C では次のようになる。
 
<sourcesyntaxhighlight lang="c">
Success: printf ("The operation was successful.\n");
</syntaxhighlight>
</source>
 
[[ALGOL|Algol 60]] 言語はラベルとして識別子も非負整数も使用可能(どちらもその後にコロンが続く)だが、多くのAlgol系言語では非負整数をラベルとして許容していない。
37 ⟶ 41行目:
=== goto ===
{{Main|Goto文}}
''goto'' 文は最も典型的な無条件の制御転送ジャンプである。キーワードとしては大文字だったり小文字だったりする(言語空白が入って<code>go to</code>だったり、単依存goだったりするが、その形式構文だいたいのものが以下のようになっている。
 
'''goto''' ''label''
 
goto 文の実行によりその次に実行する文は、ラベルが示す箇所の直後の文とる。
 
[[エドガー・ダイクストラ|ダイクストラ]]を代表とする多くの計算機科学者は、goto 文を有害だとしている。
 
=== サブルーチン ===
{{Main|サブルーチン}}
サブルーチンには、手続き、ルーチン、プロシージャ、関数(特に値を返す場合)、メソッド(に何らかの[[クラス (コンピュータ)|クラス]]に属する場合)など様々な名称がある。
 
1950年代、コンピュータのメモリは非常に小さかったため、サブルーチンの第一の目的はプログラムのサイズを削減することにあった。サブルーチンとして書かれたコードをプログラム内のあちこちから使用することでプログラム全体のコードサイズを削減したのである。現在ではサブルーチンはプログラムを構造化するために使われる。すなわち、特定のアルゴリズムを分離したり、特定のデータにアクセスするメソッドを隠蔽したりする。多数のプログラマが共同でプログラム開発をする場合、サブルーチンはある種の[[モジュール性]]を提供し、仕事の分割点の役割も果たす。
61 ⟶ 63行目:
== 必要最小限の構造化制御フロー ==
{{See also|構造化プログラミング}}
[[1966年]]、Böhm と Jacopini は Communications of the ACM 誌で論文を発表し<ref>Böhm, Jacopini. "Flow diagrams, turing machines and languages with only two formation rules" Comm. ACM, 9(5):366-371, May 1966.</ref>、'''goto''' を使って書かれたプログラムが選択 (IF THEN ELSE) とループ (WHILE condition DO xxx) のみを使って goto を使わずに書き換えられることを示した(コードの一部を複製したり、[[真理値]][[フラグ (コンピュータ)|フラグ]]変数を追加する必要がある)。後に彼らは選択もループ(と追加の真理値変数)で置き換え可能であることを示した(「'''構造化定理'''」)
 
'''非常に良く誤解されているが'''、そのような書き換えが可能という事実は、必ずし単に「[[機械語]]で書けば何で書ける」という事実と同程度の意味しかなく、それが望ましいということは全く意味したわけではかった理論的([[理論計算機科学]]的)にはコンピュータは一種類の命令 (、たとえば「subtract one number from another and branch if the result is negative) さえあれば理論的には何でもできるが([[チューリング完全]]あるいは「万能」、[[:en:One instruction set computer]] も参照)全く実用的ではなく、実際のコンピュータは多数の命令を備えているということと類似している。
 
Böhm と Jacopini の論文は全てのプログラムから goto 文を無くすことができることを示した。また、他の研究により入り口と出口がそれぞれひとつになっている制御構造が他の構造よりも理解し易いということが示された。特にそのような制御構造はプログラムの任意の箇所に制御構造を乱すことなく挿入可能な点が有利とされた。
 
また、他の研究により入り口と出口がそれぞれひとつになっている制御構造が他の構造よりも理解し易いということが示された。特にそのような制御構造はプログラムの任意の箇所に制御構造を乱すことなく挿入可能な点が有利とされた。
== 実際の制御構文 ==
 
制御構造を持つ多くのプログラミング言語は制御構造の開始を指定するためのキーワードを持つ。制御構造の終了に対応するキーワードがあるかどうかで言語は分類できる。
しかし実は、「理論に従ってgoto 文を無くしたプログラム」が「理解し易い」ものであるか否かは不明であり、実際のところ全くそのようにはならないのである{{要出典|date=2020年7月|}}。
 
== 構造化された制御要素 ==
{{seealso|構造化プログラミング|ブロック (プログラミング)|オフサイドルール|予約語}}
以下ではなぜかキーワードに変にこだわっているが、そういった字句にこだわるのではなく、構文(シンタックス)として総合的に捉えれば、たいしてこだわる意味はない。この節冒頭に挙げたリンク先の各記事を参照。
 
; 終了キーワードがない言語
: [[ALGOL|Algol 60]]、[[C言語|C]]、[[C++]]、[[Haskell]]、[[Java]]、[[Pascal]]、[[Perl]]、[[PHP: Hypertext Preprocessor(プログラミング言語)|PHP]]、[[PL/I]]、[[Python]]、[[Windows PowerShell|PowerShell]]など。この種の言語は文の並びをひとまとめ([[ブロック (プログラミング)|ブロック]])にする何らかの方法を持っている。
:* Algol 60、Pascal: <code>begin</code> ... <code>end</code>
:* C、C++、Java、Perl、PHP、PowerShell: 中括弧を使用 <code>{</code> ... <code>}</code>
86 ⟶ 93行目:
 
== 選択 ==
=== if-then-(else) ===
{{Main|if文}}
条件式と条件付き構文実行、条件節(たいていは「[[式 (プログラミング言語)]]の機能であり、プログラマが指定した[[ブーリアン型]]の「条件の評価結果の真偽によって異なる計算・処理式やブロック選択実行させる。
 
; <code>IF..GOTO</code>
: 非構造化言語に見られる形式で、典型的な機械語命令をそのまま実装し言語に持ってきたものである。条件が真なら指定されたラベル(または行番号)へジャンプ (GOTO) する。
; <code>IF..THEN..(ENDIF)</code>
: ジャンプに限らず、単純な文や入れ子になったブロックを THEN というキーワードの後に置くことができる。構造化された形式である。
; <code>IF..THEN..ELSE..(ENDIF)</code>
: 上と同じだが、条件が偽の場合の動作も記述できる。これが最も一般的な形式で、様々なバリエーションがある。終了キーワード <code>ENDIF</code> が必要な場合とそうでない場合がある。[[C言語]]やそこからの派生言語では終了キーワードは不要で、'then' に相当するキーワードも不要なことが多いが、その場合は条件式を括弧で囲む必要がある(といったような変な覚え方をするより、BNFを読んで構文規則を理解してしまったほうが早い)
 
多くの場合、条件構文は入れ子にでき、内部に他の条件構文を含むことができる。一部の言語は <code>ELSE</code> と <code>IF</code> をひとまとめにした <code>ELSEIF</code> を使用可能で、いちいち <code>ENDIF</code> または相当するキーワードを書かなくて済むようにしている。
 
==== elseif ====
「宙ぶらりんelse問題(dangling else problem、[[:en:Dangling else]])」も関係するのだが、文法の設計(デザイン)によっては、
IF cond THEN
...
ELSE
IF cond THEN
...
ELSE
IF cond THEN
...
FI
FI
FI
のように、「複数の場合に対する場合分け」の単純な多分岐であるにもかかわらず、どんどんネストが深くなるような書き方をせざるをえない場合がある(C言語の文法の設計は、こうなってはいない)。これは ELSEIF のようなキーワードの導入で解決できる。<code>elseif</code>, <code>elsif</code>, <code>elif</code> など言語によるバリエーションが多いので、テキストエディタによるリアルタイムなシンタックスハイライトが非常に有効である。言語によっては <code>else if</code> という「2語から成るキーワードのようなもの」という設計のものもある。
{| class="wikitable"
|-
106 ⟶ 125行目:
! [[Python]]:
! [[LISP|Lisp]]:
! [[Smalltalk]]:
 
|-
|<sourcesyntaxhighlight lang="pascal">
if a > 0 then begin
writeln("yes")
114 ⟶ 133行目:
writeln("no")
end
</syntaxhighlight>
</source>
|<sourcesyntaxhighlight lang="c">
if (a > 0) {
printf("yes");
121 ⟶ 140行目:
printf("no");
}
</syntaxhighlight>
</source>
|<sourcesyntaxhighlight lang="bash">
if [ $a -gt 0 ]
then
129 ⟶ 148行目:
echo "no"
fi
</syntaxhighlight>
</source>
|<sourcesyntaxhighlight lang="python">
if a > 0:
print "yes"
else:
print "no"
</syntaxhighlight>
</source>
|<sourcesyntaxhighlight lang="lisp">
(princ
(if (plusp a)
"yes"
"no"))
</syntaxhighlight>
</source>
|<syntaxhighlight lang="smalltalk">
Transcript show:
(
a > 0
ifTrue:
[
'yes'
]
ifFalse:
[
'no'
]
).
</syntaxhighlight>
|}
 
あまり一般的でないバリエーションとして、以下のような例がある。
* [[FORTRAN]]などの一部の言語では、3方向の分岐を扱う「算術IF文」があり、数値を正か、ゼロか、負か判定して処理を分岐させる。
* 一部多く[[関数型言語]]などでは<code>if文</code>が関数や式として実装されており、例えば[[LISP]] <code>cond</code> があるようなifは評価した式の結果を返す
* 一部の言語では<code>if文</code>が[[演算子 (コンピュータ言語)|演算子]]としての様に実装されており、例えばC言語の[[条件演算子]]がある。
* [[Perl]]ではC言語風の <code>if</code> だけでなく、<code>when</code> と <code>unless</code> や、コードの後に条件式が来る <code>if</code> がある。
* [[Smalltalk]]では言語組込み基本構文機能としてではなく、<code>ifTrue</code> と <code>ifFalse</code> というメッセージに手続き引数を与えること条件付き実行を実装していができる。
一般論として、関数の引数を積極評価してしまう言語では、条件実行のようなものを関数にできない。遅延評価のような機構が何かあれば、条件実行を特に言語機能にしなくても、引数を遅延評価する関数によって、条件実行もできる。
 
=== パターンマッチング ===
{{see|パターンマッチング}}
<!--[[パターンマッチング]]とは、[[ML (プログラミング言語)|ML]]のような一部のプログラミング言語での高度に抽象化された if-then-else の名称である。--><!--「高度に抽象化された if-then-else の名称」って何?-->ここでは[[OCaml]]言語での例を挙げる。
 
'''match''' fruit '''with'''
'''|''' "apple" '''<nowiki>-></nowiki>''' cook pie
'''|''' "coconut" '''<nowiki>-></nowiki>''' cook dango_mochi
'''|''' "banana" '''<nowiki>-></nowiki>''' mix''';;'''
 
=== case文とswitchとcase ===
{{Main|switch文}}
[[switch文]](言語によっては「case文」)は、指定された値を指定された定数群と比較し、最初に一致した定数に従ってその後の処理を決定するものである。一般にどの定数とも一致しなかった場合を想定したデフォルト動作を 'else' や 'otherwise' などとして用意しておく。[[ルックアップテーブル]]などを使った[[コンパイラ最適化]]が可能である。[[動的プログラミング言語]]では比較対象が定数式である必要はなく、[[パターンマッチング|パターンマッチ]]に拡張することが可能である。例えば下記の[[シェルスクリプト]]の例で <code>'*)'</code> は任意の文字列にマッチングする[[正規表現]]を使ってデフォルト動作を指定している。[[SQL]]の <code>decode</code> のように、関数形式で実装することのような見た目のできのもある。
 
{| class="wikitable"
169 ⟶ 203行目:
! [[シェルスクリプト]]:
|-
|<sourcesyntaxhighlight lang="pascal">
case someChar of
'a': actionOnA;
176 ⟶ 210行目:
else actionOnNoMatch;
end;
</syntaxhighlight>
</source>
|<sourcesyntaxhighlight lang="c">
switch (someChar) {
case 'a': actionOnA; break;
185 ⟶ 219行目:
default: actionOnNoMatch;
}
</syntaxhighlight>
</source>
|<sourcesyntaxhighlight lang="bash">
case $someChar in
a) actionOnA ;;
193 ⟶ 227行目:
*) actionOnNoMatch ;;
esac
</syntaxhighlight>
</source>
|}
 
201 ⟶ 235行目:
ループはソースコード上で1回だけ書かれた文の並びを連続して複数回実行することである。ループの「中」のコード(本体と呼び、下記の例では ''xxx'' で表されている)は指定回数実行されるか、指定されたコレクションの各要素に対応して実行されるか、何らかの条件が成立するまで繰り返し実行される。[[無限ループ|無限に]]繰り返されることもある。
 
[[Scheme]]や[[Haskell]]のような[[関数型言語]]では、ループは明確なループ用構文ではなくより[[再帰呼び出し]]や[[不動点コンビネータ]]を使用して実現されプログラミングすこと多い普通である。[[末尾再帰]]は再帰呼び出しの特殊ケースであり、容易にループに変換できる(正確には「末尾呼び出しは容易にジャンプに変換できる。末尾再帰は末尾呼び出しの対象が自身になっているという特殊ケースであり、ループに変換できる」)
 
=== カウント制御ループ ===
{{Main|for文}}
多くのプログラミング言語は指定された回数だけループブロックを繰り返す構文ループである。本来、その回数だけ指定するなどもと抽象化されているべきであるが、「ループ変数」などを指定するなど煩雑さがともなっているものが多い。以下の例で N が 1 より小さい場合、ループ本体は全く実行されない。カウントは多くの場合増える方向だけでなく減る方向にも設定可能で、1回に増える量も 1 以外に設定できることが多い(Pascalだけが±1にしかできない)。
 
FOR I = 1 TO N '''for''' I := 1 '''to''' N '''do''' '''begin'''
211 ⟶ 245行目:
NEXT I '''end''';
DO I = 1,N '''for''' ( I=1; I<nowiki><=</nowiki>N; ++I ) {
xxx xxx
END DO }
 
多くのプログラミング言語では、カウント制御ループでは整数のみが使われる。浮動小数点数はハードウェアの制限により精度に限界がある(「ハードウェアの制限により」ではない。そもそも間違ったプログラミングなのである)。従って次のようなループでは、
'''for''' X := 0.1 '''step''' 0.1 '''to''' 1.0 '''do'''
繰り返し回数が9回の場合と10回の場合がある。これは丸め誤差やハードウェアやコンパイラの違いによって変わってくる(といったように過去には信じている者が多かった。現代ではIEEE 754で標準化されているのだが、このようなプログラミングをしてはならないことに変わりはない)。さらに言えば、Xに繰り返し加算すると丸め誤差が累積していき、想定した数列である 0.1, 0.2, 0.3, ..., 1.0 からかけ離れていくことがありうる(「ありうる」ではなく、「そうなる」と表現すべき、浮動小数点表現による現象である。なお、このような例ばかりを想定して、十進浮動小数点では誤差が出ない、とナイーブに考える者が非常に多いが、間違いである)
 
=== 条件制御ループ ===
{{Main|while文|do-while文}}
また、多くのプログラミング言語は条件(条件式)が指定されており、その式を評価した条件結果変化す真であれば(あいは、偽あれば)ループを繰り返す構文をもっている。条件のテストがループの先頭にある場合と最後にある場合がある。前者の場合、ループ本体を全く実行しないことがありうるが、後者の場合は少なくとも1回はループ本体を実行する。
 
DO WHILE (test) '''repeat'''
246 ⟶ 280行目:
=== コレクション制御ループ ===
{{Main|Foreach文|イテレータ}}
一部のプログラミング言語(例えば、[[Ada]]、[[D言語]]、[[Smalltalk]]、[[PHP: Hypertext Preprocessor(プログラミング言語)|PHP]]、[[Perl]]、[[Object Pascal]]、[[Java]]、[[C Sharp|C#]]、[[Microsoft Visual Basic|Visual Basic]]、[[Ruby]]、[[Python]]、[[JavaScript]]、[[FORTRAN|Fortran 95]] およびそれ以降)では、明示的に配列や集合やコレクションの全要素に対応してループを回すことができる。
 
someCollection '''do''': [:eachElement |xxx].
256 ⟶ 290行目:
'''foreach''' someArray { xxx }
'''foreach''' (someArray as $k <nowiki>=></nowiki> $v) { xxx }
Collection<String> coll; '''for''' (String s : coll) {}
266 ⟶ 300行目:
'''forall''' ( index = first:last:step... )
 
=== 汎用繰り返し構文 ===
C言語の '''for''' 文や [[Common Lisp]] の '''do''' のような汎用性の高い繰り返し構造要素を使えば、前述の各種ループもその他のループも実現できる。例えば、複数のコレクションを並列に回したりできる。もっとも、個別のループ構造がある場合、汎用繰り返し構文よりもそちらを使った方がコードの目的をより明確に表現できるとも言える。
 
=== 無限ループ ===
277 ⟶ 311行目:
{{Main|continue文}}
 
ループ途中でループ処理を中断してループの先頭に戻り、次の繰り返しを開始したい場合がある。言語によってはこれを実現する <code>continue</code> とか <code>skip</code>、<code>next</code> といった文を用意している。その効果は最も内側のループ本体の実行を途中で止め、そのループの次の繰り返しを最初から行う。もしそのときの実行が最後の繰り返しであった場合、ループそのものを早期に終了させるのと同じことになる。
 
=== 現在の繰り返しの再実行 ===
290 ⟶ 324行目:
カウント制御型ループを使って配列上のデータを検索している際に、必要な要素を見つけたら即座にループから抜け出したいという状況がありうる。プログラミング言語によっては <code>break</code> とか <code>exit</code>、<code>last</code> といった文を用意していて、現在のループを即座に抜けてそのループの直後の文に制御を転送する機能を持っている。サブルーチン内のループで <code>return</code> を使えば、入れ子になったループからも脱出することになる。多次元配列を入れ子になったループで検索している場合、若干複雑になる(「提案された制御構造」の章参照)。
 
以下の例は[[Ada]]を使ったものである。Ada は「ループからの早期脱出」と「[[制御構造#途中にテストのあるループ|途中にテストのあるループ]]」の両方をサポートしている。どちらもよく似ているが、コードを比較すればその違いがわかる。「早期脱出」では '''if''' 文を使うのいずれして「途中のテスト」は独自汎用制御造であるifを使との組み合わせによるものか、専の制御構造によものか、という違いでしかない
 
<sourcesyntaxhighlight lang="ada">
with Ada.Text IO;
with Ada.Integer Text IO;
306 ⟶ 340行目:
end loop Read_Data;
end Print_Squares;
</syntaxhighlight>
</source>
 
[[Python]] は <code>break</code> でループを早期脱出したか否かに依存して実行するかどうかが決定される構文ブロック用意してい指定できる。以下はその例である。
 
<sourcesyntaxhighlight lang="python">
for n in set_of_numbers:
if isprime(n):
317 ⟶ 351行目:
else:
print "Set did not contain any prime numbers"
</syntaxhighlight>
</source>
 
Python では <code>for</code> 文も <code>while</code> 文もこのような <code>else</code> 節を使うことができる。else 節は早期脱出が発生しなかったときのみ実行される。
 
=== ループ変化条件とループ不変条件 ===
{{仮リンク|ループ変化条件|en|Loop variant}}と{{仮リンク|[[ループ不変条件|en|loop invariant}}]]は、ループの正しさを表すのに使われる<ref>{{Cite book|last=Meyer|first=Bertrand|title=Eiffel: The Language|year=1991|publisher=Prentice Hall|pages=129–129-131}}</ref>。
 
現実的には、ループ変化条件とは非負の初期値を持つ整数式である。変化条件はループを回るたびに減少しなければならないが、正しいループ実行の間は負の値になってはならない。ループ変化条件はループが終了するであろうことを保証するのに使われる。
331 ⟶ 365行目:
 
=== サブ言語としてのループ ===
一部の[[LISPLisp]]方言では、ループを記述するための幅広い多機能なサブ言語を提供していことが多い初期の代表的な例としては [[Interlisp]] の Conversional Lisp がある。[[Common Lisp]] では loopLOOP マクロを使ってそのようなサブ言語を実装していが挙げられる<ref>{{Cite web|title=Common Lisp LOOP macro|url= http://www.lispworks.com/documentation/HyperSpec/Body/m_loop.htm |accessdate=2012-09-08}}</ref>。初期の例としては [[Interlisp]] の Conversional Lisp がある
 
=== ループ機能の比較表 ===
370 ⟶ 404行目:
|
|
|-
| [[C言語|C]]
| {{Yes}}
385 ⟶ 419行目:
|
|
|-
| [[C++]]
| {{Yes}}
400 ⟶ 434行目:
|
|
|-
| [[C Sharp|C#]]
| {{Yes}}
408 ⟶ 442行目:
| {{Yes}}
| {{Yes}}
| {{Yes}} <!-- for(;;){} による無限ループは可能 -->
| {{No}}
| {{Partial|深い入れ子}} <ref name="deep_break_c" group="※" />
| {{Partial|深い入れ子}} <ref name="deep_break_c" group="※" />
425 ⟶ 459行目:
| {{Yes}}
| {{Yes|深い入れ子}}
| {{Yes}} <ref group="※">LOOP は独自の分岐構文を内包している</ref>
| {{No}}
|
|
565 ⟶ 599行目:
|
|
|-
| [[PHP: Hypertext Preprocessor(プログラミング言語)|PHP]]
| {{Yes}}
| {{No}}
580 ⟶ 614行目:
|
|
|-
| [[Perl]]
| {{Yes}}
633 ⟶ 667行目:
| {{Yes}}
| {{No}}
| {{No}}<ref group="※">ただし、標準ライブラリに無限ループを実現する[http://docs.ruby-lang.org/ja/2.1.0/method/Kernel/m/loop.html loopメソッド]が存在する。</ref>
| {{Yes}}
| {{Partial|深い入れ子}} <ref name="deep_break_e" group="※" />
| {{Partial|深い入れ子}} <ref name="deep_break_e" group="※" />
670 ⟶ 704行目:
|
|
|-
| [[Windows PowerShell]]
| {{Yes}}
687 ⟶ 721行目:
|}
{{Reflist|group="※"|refs=
<ref name="loop_infinite" group="※"><code>while (true)</code> は構文としては無限ループ専用に用意された構文ではないので、ここでは無限ループに含めていない。一方、<code>for (式;;式)</code> は無限ループ専用構文とみなしている</ref>
<ref name="loop forloop_for" group="※">C言語の <code>for (''init''; ''test''; ''increment'')</code> は汎用ループ構文であり、カウント制御専用ではないが、カウント制御として使われることが多い。</ref>
<ref name="deep_break_c" group="※">C、C++、C# での深い入れ子からの脱出は、ラベルとgoto文を使用する。</ref>
<ref name="loop_phpforeach" group="※">オブジェクト群のイテレーションは PHP 5 で[http://www.php.net/manual/en/language.oop5.iterations.php 追加された]。</ref>
<ref name="loop_range" group="※">カウントループは例えばPythonの <code>range()</code> を使って incrementing list や generator でシミュレートされる。</ref>
<ref name="deep_break_e" group="※">深いブレイクを実現するには、例外処理を活用する必要がある。</ref>
<ref name="while" group="※">言語構文としては存在せず、<code>while</code> 関数を使用する(関数ではないが、関数だと誤解している者が多い)。</ref>
<ref name="user" group="※">言語構文としては存在しないが、ユーザーが汎用ループ関数を定義できる。</ref>
<ref name="count_loop_eiffel" group="※">カウント制御ループは整数 interval によるイテレーションで実現される。早期脱出は exit に条件を追加することでなされる。</ref>
<ref name="retry_in_eiffel" group="※">Eiffelには <code>retry</code> という予約語があるが、これはループ制御用ではなく[[例外処理]]用である。</ref>
<ref name="requires_JML" group="※">[[:en:Java Modeling Language|Java Modeling Language]] (JML) が必要</ref>
<ref name="integer_variant" group="※">ループ変化条件は整数でなければならず、超限的変化条件はサポートしていない[http://archive.eiffel.com/doc/faq/variant.html]</ref>
}}
 
720 ⟶ 754行目:
最近の言語は<code>GOTO</code>文を使用せずに例外処理を行う構造化された制御構造を備えている。
 
<sourcesyntaxhighlight lang="cpp">
try {
xxx1 // この中のどこかで以下を使用する
732 ⟶ 766行目:
actionForAnythingElse
}
</syntaxhighlight>
</source>
 
任意の '''catch''' 節が上記の例では使用されている。D言語、Java、C#、Python では <code>try</code> 構造に <code>finally</code> 節を追加することができる。<code>try</code> 部分を離れる際にはどういう理由であっても必ず <code>finally</code> 節が実行されることが保証されている。これは処理を終了する際に何らかの高価な資源(オープン中のファイルやデータベース接続)を解放しなければならない場合に便利である。
 
<sourcesyntaxhighlight lang="csharp">
FileStream stmstream = nullnew FileStream ("logfile.txt", FileMode.Create); // C# の例
try {
return ProcessStuff(stream); // 例外を発生する可能性がある
stm = new FileStream ("logfile.txt", FileMode.Create);
return ProcessStuff(stm); // 例外を発生する可能性がある
} finally {
ifstream. Close(stm != null);
stm. Close();
}
</syntaxhighlight>
</source>
 
この例は非常に一般的であり、C# ではこのための特別な構文がある。
 
<sourcesyntaxhighlight lang="csharp">
using (FileStream stmstream = new FileStream ("logfile.txt", FileMode.Create)) {
return ProcessStuff(stmstream); // 例外を発生する可能性がある
}
</syntaxhighlight>
</source>
 
上記の例の <code>using</code> ブロックを離れるとき、コンパイラが自動的に <code>stm</code> オブジェクトを解放する。Pythonの <code>with</code> 文やRubyの <code>File.open</code> へのブロック引数も同様の効果がある。
763 ⟶ 795行目:
[[AppleScript]] [[スクリプト言語]] は "<code>try</code>" ブロックにいくつかの情報を提供する。
 
<sourcesyntaxhighlight lang = "applescript">
try
set myNumber to myNumber / 0
769 ⟶ 801行目:
if ( e = "Can't divide by zero" ) then display dialog "You must not do that"
end try
</syntaxhighlight>
</source>
 
=== 継続 ===
785 ⟶ 817行目:
| {{No}}
| {{Yes}}
|-
| [[C言語|C]]
| {{No}}
| {{No}}
|-
| [[C++]]
| {{No}}
805 ⟶ 837行目:
| {{No}}
| {{Yes}}
|-
| [[Eiffel]]
| {{No}}
822 ⟶ 854行目:
| {{Yes}}
|-
| [[PHP: Hypertext Preprocessor(プログラミング言語)|PHP]]
| {{No}}
| {{Yes}}
852 ⟶ 884行目:
 
== 提案された制御構造 ==
[[ドナルド・クヌース]]は1974年の論文 "Structured Programming with go to Statements"<ref>Knuth, Donald E. "Structured Programming with go to Statements" =ACM Computing Surveys 6(4):261-301, December 1974.</ref> でそれまでの制御構造でカバーされていない2種類の状況を提示し、それを実現する制御構造を例示した。<!--主流の言語で実装された例は無いなどと書かれていたが、Visual Basicの「Do...Loop ステートメント」がほぼそれである。-->他にも以下に示すような提案がある。
Datamation 誌(1973年12月)に掲載されたふざけた記事で<ref>[http://www.fortran.com/fortran/come_from.html We don't know where to GOTO if we don't know where we've COME FROM. This (spoof) linguistic innovation lives up to all expectations.] By R. Lawrence Clark* From DATAMATION, December, 1973</ref>、R. Lawrence Clark は GOTO文を COMEFROM文で置き換えることを提案し、面白い例をいくつか提示した。これは実際に [[INTERCAL]] という言語で実装された。この言語は[[難解プログラミング言語|プログラムを可能な限り読みにくくする]]よう設計されている。
 
[[ドナルド・クヌース]]は1974年の論文 "Structured Programming with go to Statements"<ref>Knuth, Donald E. "Structured Programming with go to Statements" =ACM Computing Surveys 6(4):261-301, December 1974.</ref> でこれまでの説明された制御構造でカバーされていない2種類の状況を提示し、それを実現する制御構造を例示した。有用性にも関わらず、これらの構文は主流のプログラミング言語に実装された例はない。
 
=== 途中にテストのあるループ ===
865 ⟶ 895行目:
'''repeat'''; '''repeat''';
 
もし ''xxx1'' が省略されたら、テストが先頭にあるループとなる。もし ''xxx2'' が省略されたら、テストが最後尾にあるループとなる。''while'' が省略されれば無限ループとなる。このように、このひとつな一種類の制御構、必要な多くのタイログラミのループのパターを表現できることが示されたことから、以降の言語にある複数ではこ制御ような汎用性の高いループ造を持つも代替となもあ(VBのDo...Loopなど)。ありうべき派生としてループ内に複数の '''while''' テストを配置することを許すことが考えられるが、その場合は後述の '''exitwhen''' の方が適切である。
 
一般に、任意のループ構造と、条件分岐と'''break'''を組み合わせて、同様のプログラムを書ける(これは要するに、どんな制御構造も、コンパイルされれば機械語では分岐命令になる、ということと同様のことを言っている)。
このような構文を持たない言語では、一般に無限ループとブレイクの組み合わせて等価なコードをエミュレートできる。
 
'''while''' (true) {
876 ⟶ 906行目:
}
 
[[Ada]]言語では、上記のループ構造('''loop'''-'''while'''-'''repeat''')の代替として標準の無限ループ('''loop'''-'''end loop''')内で '''exit when'''節を使うことで同様の制御構造を実現できる(後述の '''exitwhen''' 文と混同しないよう注意されたい)。
 
<sourcesyntaxhighlight lang="ada">
with Ada.Text_IO;
with Ada.Integer_Text_IO;
892 ⟶ 922行目:
end loop Read_Data;
end Print_Squares;
</syntaxhighlight>
</source>
 
ループの命名(この例では ''Read_Data'')は必須ではないが、ループの入れ子で外側のループまで脱出させることができる。
922 ⟶ 952行目:
missing: print ("item is not in table");
'''endexit''';
 
=== COMEFROM ===
{{main|comefrom文}}
Datamation誌(1973年12月)に掲載された記事で<ref>[http://www.fortran.com/fortran/come_from.html We don't know where to GOTO if we don't know where we've COME FROM. This (spoof) linguistic innovation lives up to all expectations.] By R. Lawrence Clark* From DATAMATION, December, 1973</ref>、R. Lawrence Clark は COME FROM 文を提案し、面白い例をいくつか提示した。それ自体は「GO TO論争に寄与する」と称したジョークであるが、[[ジャーゴンファイル]]の記事が指摘しているように<ref>http://catb.org/jargon/html/C/COME-FROM.html</ref>、たとえばFortranのDO文は「そこで指定した行番号のある行からそこに飛ぶ」という一種のCOMEFROMであることなど、制御構造の問題に面白い視点を与えるものではある。setjmp/longjmpと関連させた指摘もある<ref>http://www.nurs.or.jp/~sug/soft/super/longjmp.htm#sec36</ref>。
 
COMEFROM文は[[INTERCAL]]という[[難解プログラミング言語]]に実装された(INTERCALの実装者は[[ジャーゴンファイル]]の編集者でもある)。
 
== 脚注 ==
933 ⟶ 969行目:
== 関連項目 ==
* [[分岐命令]]
* [[Goto文|goto文]]
* [[サブルーチン]]
* [[イベントループ]]
947 ⟶ 983行目:
 
== 外部リンク ==
* [http://www.acm.org/classics/oct95/ Go To Statement Considered Harmful]
* [http://www.fortran.com/fortran/come_from.html A Linguistic Contribution of GOTO-less Programming]
* [http://pplab.snu.ac.kr/courses/adv_pl04/papers/p261-knuth.pdf Structured Programming with Go To Statements]
* [http://www.bitsavers.org/pdf/ibm/704/24-6661-2_704_Manual_1955.pdf IBM 704 Manual]
 
{{DEFAULTSORT:せいきよこうそう}}
[[Category:制御構造|*]]
[[Category:プログラミング言語の構文|せいきよこうそう]]