LINEスタンプ制作代行サービス・LINEスタンプの作り方!

お電話でのお問い合わせ:03-6869-8600

stampfactory大百科事典

制御構造

制御構造(せいぎょこうぞう)あるいは制御フローとは、計算機科学において文や命令、また命令型プログラミングや宣言型プログラミングにおけるサブルーチン呼び出しを実行または評価する順序を意味する。命令型プログラミング言語では、制御構文(せいぎょこうぶん)とは命令の実行順序を通常の逐次実行以外の順番に変化させる構文であり、2つ以上の経路のいずれかを選択するものである。正格でない関数型言語では、関数や言語構成物が同様の結果を達成するために存在しているが、それらは必ずしも制御構文とは呼ばれない。制御構文の種類は言語によって様々だが、大まかに以下のように分類できる。割り込みとシグナルは制御フローを変化させる別の機構であり、サブルーチンに似ているが、言語内の制御構文ではなく外部のイベントなどの結果として非同期に発生するものである。自己書き換えコードも副作用によって制御フローを変化させることができる。ただし、それを制御構文として明確化することはほとんどない。機械語やアセンブリ言語では、制御命令はプログラムカウンタを変更する働きを持つ。多くのCPUでは制御命令として(条件付きおよび無条件の)分岐命令しか用意されていない。ラベルとは、ソースコード上の固定の位置を示す何らかの名前(あるいは数値)であり、ソースコード上の他の箇所の制御構文で参照される。ソースコード上の位置を記録する以外の効果はない。行番号は一部の言語(FORTRANやBASIC)でラベルの一種として使われ、負でない整数がソースコードの各テキスト行の先頭に置かれる。行番号を使用する言語では、連続で実行される文には行番号が増えるように行番号を与える必要がある。ただし、連続な番号である必要はない。例えば BASIC では次のようになっている。CやAdaといった言語のラベルは識別子であり、文の前に書かれ、その直後にコロンが書かれる。例えば C では次のようになる。Algol 60 言語はラベルとして識別子も非負整数も使用可能(どちらもその後にコロンが続く)だが、多くのAlgol系言語では非負整数をラベルとして許容していない。"goto" 文は最も典型的な無条件の制御転送である。キーワードとしては大文字だったり小文字だったりする(言語に依存する)が、その形式は以下のようになっている。goto 文は、次に実行する文をラベルが示す箇所の直後の文とする。ダイクストラを代表とする多くの計算機科学者は、goto 文を有害だとしている。サブルーチンには、手続き、ルーチン、プロシージャ、関数(特に値を返す場合)、メソッド(特に何らかのクラスに属する場合)など様々な名称がある。1950年代、コンピュータのメモリは非常に小さかったため、サブルーチンの第一の目的はプログラムのサイズを削減することにあった。サブルーチンとして書かれたコードをプログラム内のあちこちから使用することでプログラム全体のコードサイズを削減したのである。現在ではサブルーチンはプログラムを構造化するために使われる。すなわち、特定のアルゴリズムを分離したり、特定のデータにアクセスするメソッドを隠蔽したりする。多数のプログラマが共同でプログラム開発をする場合、サブルーチンはある種のモジュール性を提供し、仕事の分割点の役割も果たす。サブルーチンに引数があればさらに便利になる。多くのプログラミング言語には平方根を求めるサブルーチンが組み込まれており、引数として平方根を求めたい数を与えることができる。プログラミング言語によっては再帰呼び出しが可能である。つまり、サブルーチンが直接的あるいは間接的に自分自身を呼び出すことができる。クイックソートや木構造を探索するアルゴリズムなどは再帰を使った方が素直に表現できる。サブルーチンを使用すると、引数の受け渡し、サブルーチン呼び出し、コールスタック処理、サブルーチンからの復帰などのオーバヘッドによりプログラム性能が若干低下する。実際のオーバヘッドはハードウェアおよびソフトウェアのアーキテクチャに依存する。コンパイラによってはインライン展開を効果的に使用してオーバヘッドの低減を図るものもある。プログラミング言語によってはサブルーチンの物理的な最後尾に到達しないとサブルーチンから復帰できない方式のものもある。他の言語には return や exit 文がある。これはサブルーチンの最後尾への分岐と等価であり、制御構造を複雑化するものではない。必要に応じて複数のそれらの文をサブルーチン内に置くことができる。1966年、Böhm と Jacopini は Communications of the ACM 誌で論文を発表し、goto を使って書かれたプログラムが選択 (IF THEN ELSE) とループ (WHILE condition DO xxx) のみを使って goto を使わずに書き換えられることを示した(コードの一部を複製したり、真理値フラグ変数を追加する必要がある)。後に彼らは選択もループ(と追加の真理値変数)で置き換え可能であることを示した。非常に良く誤解されているが、そのような書き換えが可能という事実は、単に「機械語で書けば何でも書ける」という事実と同程度の意味しかなく、それが望ましいということは全く意味しない。理論的(理論計算機科学的)にはコンピュータは一種類の命令、たとえば「subtract one number from another and branch if the result is negative」さえあれば何でもできるが(チューリング完全あるいは「万能」、 も参照)全く実用的ではなく、実際のコンピュータは多数の命令を備えているということと類似している。Böhm と Jacopini の論文は全てのプログラムから goto 文を無くすことができることを示した。また、他の研究により入り口と出口がそれぞれひとつになっている制御構造が他の構造よりも理解し易いということが示された。特にそのような制御構造はプログラムの任意の箇所に制御構造を乱すことなく挿入可能な点が有利とされた。しかし実は、「理論に従ってgoto 文を無くしたプログラム」が「理解し易い」ものであるか否かは不明であり、実際のところ全くそのようにはならないのである。制御構造を持つ多くのプログラミング言語は制御構造の開始を指定するためのキーワードを持つ。制御構造の終了に対応するキーワードがあるかどうかで言語は分類できる。条件式と条件付き構文はプログラミング言語の機能であり、プログラマが指定したブーリアン型の「条件」の評価結果の真偽によって異なる計算・処理を実行させる。多くの場合、条件構文は入れ子にでき、内部に他の条件構文を含むことができる。一部の言語は codice_34 と codice_17 をひとまとめにした codice_36 を使用可能で、いちいち codice_18 または相当するキーワードを書かなくて済むようにしている。あまり一般的でないバリエーションとして、以下のような例がある。パターンマッチングとは、MLのような一部のプログラミング言語での高度に抽象化された if-then-else の名称である。ここではOCaml言語での例を挙げる。switch文(言語によっては「case文」)は、指定された値を指定された定数群と比較し、最初に一致した定数に従ってその後の処理を決定するものである。一般にどの定数とも一致しなかった場合を想定したデフォルト動作を 'else' や 'otherwise' などとして用意しておく。ルックアップテーブルなどを使ったコンパイラ最適化が可能である。動的プログラミング言語では比較対象が定数式である必要はなく、パターンマッチに拡張することが可能である。例えば下記のシェルスクリプトの例で codice_44 は任意の文字列にマッチングする正規表現を使ってデフォルト動作を指定している。SQLの codice_45 文のように、関数形式で実装することもできる。ループはソースコード上で1回だけ書かれた文の並びを連続して複数回実行することである。ループの「中」のコード(本体と呼び、下記の例では "xxx" で表されている)は指定回数実行されるか、指定されたコレクションの各要素に対応して実行されるか、何らかの条件が成立するまで繰り返し実行される。無限に繰り返されることもある。SchemeやHaskellのような関数型言語では、ループは明確なループ用構文ではなく再帰呼び出しや不動点コンビネータを使用して実現されることが多い。末尾再帰は再帰呼び出しの特殊ケースであり、容易にループに変換できる。多くのプログラミング言語は指定された回数だけループを繰り返す構文を持っている。以下の例で N が 1 より小さい場合、ループ本体は全く実行されない。カウントは多くの場合増える方向だけでなく減る方向にも設定可能で、1回に増える量も 1 以外に設定できることが多い(Pascalだけが±1にしかできない)。多くのプログラミング言語では、カウント制御ループでは整数のみが使われる。浮動小数点数はハードウェアの制限により精度に限界がある。従って次のようなループでは、繰り返し回数が9回の場合と10回の場合がある。これは丸め誤差やハードウェアやコンパイラの違いによって変わってくる。さらに言えば、Xに繰り返し加算すると丸め誤差が累積していき、想定した数列である 0.1, 0.2, 0.3, ..., 1.0 からかけ離れていくことがありうる。また、多くのプログラミング言語は指定した条件が変化するまでループを繰り返す構文をもっている。条件のテストがループの先頭にある場合と最後にある場合がある。前者の場合、ループ本体を全く実行しないことがありうるが、後者の場合は少なくとも1回はループ本体を実行する。は通常のループ内で値の変化を検出する手段として使われ、値のグループの処理のトリガーとなる。ループ内で変化する値(群)をキーで監視し、可変な値に関連したグループイベント処理へとプログラムのフローを変換する。一部のプログラミング言語(例えば、Ada、D言語、Smalltalk、、Perl、Object Pascal、Java、C#、Visual Basic、Ruby、Python、JavaScript、Fortran 95 およびそれ以降)では、明示的に配列や集合やコレクションの全要素に対応してループを回すことができる。C言語の for 文や Common Lisp の do のような汎用繰り返し構造を使えば、前述の各種ループもその他のループも実現できる。例えば、複数のコレクションを並列に回したりできる。もっと個別のループ構造がある場合、汎用繰り返し構文よりもそちらを使った方がコードの目的をより明確に表現できる。場合によっては無限にループする方がプログラムに適していることもあるし、何らかのエラーが発生するまでループするという場合もある。実際、イベント駆動型プログラム(サーバなど)はイベント制御ループを永遠に回り続け、プロセスが操作者によって終了させられたときだけループを停止する。ただし一般には、無限ループはプログラミングのミスで発生する。すなわち、ループ終了条件がループ内で全く発生しないことが原因で意図しない無限ループとなる。ループ途中でループ処理を中断してループの先頭に戻り、次の繰り返しを開始したい場合がある。言語によってはこれを実現する codice_46 とか codice_47、codice_48 といった構文を用意している。その効果は最も内側のループ本体の実行を途中で止め、そのループの次の繰り返しを最初から行う。もしそのときの実行が最後の繰り返しであった場合、ループそのものを早期に終了させるのと同じことになる。Perl や Ruby といった一部の言語では codice_49 文によって現在の繰り返しを先頭から再実行することができる。Ruby では、codice_50 文でループ全体を最初から再実行することができる。カウント制御型ループを使って配列上のデータを検索している際に、必要な要素を見つけたら即座にループから抜け出したいという状況がありうる。プログラミング言語によっては codice_51 とか codice_52、codice_53 といった文を用意していて、現在のループを即座に抜けてそのループの直後の文に制御を転送する機能を持っている。サブルーチン内のループで codice_54 を使えば、入れ子になったループからも脱出することになる。多次元配列を入れ子になったループで検索している場合、若干複雑になる(「提案された制御構造」の章参照)。以下の例はAdaを使ったものである。Ada は「ループからの早期脱出」と「途中にテストのあるループ」の両方をサポートしている。どちらもよく似ているが、コードを比較すればその違いがわかる。「早期脱出」では if 文を使うのに対して、「途中のテスト」は独自の構文を使用する。Python は codice_51 文でループを早期脱出したか否かに依存して実行するかどうかが決定される構文を用意している。以下はその例である。Python では codice_56 文も codice_57 文もこのような codice_58 節を使うことができる。else 節は早期脱出が発生しなかったときのみ実行される。とループ不変条件は、ループの正しさを表すのに使われる。現実的には、ループ変化条件とは非負の初期値を持つ整数式である。変化条件はループを回るたびに減少しなければならないが、正しいループ実行の間は負の値になってはならない。ループ変化条件はループが終了するであろうことを保証するのに使われる。ループ不変条件は、ループを回る前と各反復において真でなければならない表明である。すなわち、ループが正しく終了するには終了条件とループ不変条件が共に真でなければならない。ループ不変条件は、ループ実行中にループの具体的属性を監視するのに使われる。Eiffelなどのプログラミング言語でループ変化条件とループ不変条件がサポートされている。Javaではアドオンである (JML) という仕様で同様のものをサポートしている。一部のLISP方言では、ループを記述するための幅広いサブ言語を提供している。初期の例としては Interlisp の Conversional Lisp がある。Common Lisp では loop マクロを使ってそのようなサブ言語を実装している。多くのプログラミング言語、特に動的なプログラミングスタイルを指向した言語では、「非局所制御フロー」の構造を持っている。これを使うと実行の流れは現在のコンテキストから離れ、事前に定義された場所から続行される。「条件」、「例外」、「継続」の3種類の典型的な非局所制御構造がある。PL/I は標準で22種類の条件(ZERODIVIDE、SUBSCRIPTRANGE、ENDFILE など)をサポートし、これを発生(RAISE)させ、ON "condition" action; で解釈することができる。プログラマは独自の条件を定義することもできる。構造無しの IF 文のように action にはひとつの文しか書けないので、多くの場合 GOTO 文を使って制御フローを継続する必要がある。しかし、実装によってはこれは空間と時間を無視できないくらい浪費する(特に SUBSCRIPTRANGE の場合)。多くのプログラマは条件を使わないようコードを書くことが多かった。典型的な文法例:最近の言語はcodice_59文を使用せずに例外処理を行う構造化された制御構造を備えている。任意の catch 節が上記の例では使用されている。D言語、Java、C#、Python では codice_60 構造に codice_61 節を追加することができる。codice_60 部分を離れる際にはどういう理由であっても必ず codice_61 節が実行されることが保証されている。これは処理を終了する際に何らかの高価な資源(オープン中のファイルやデータベース接続)を解放しなければならない場合に便利である。この例は非常に一般的であり、C# ではこのための特別な構文がある。上記の例の codice_64 ブロックを離れるとき、コンパイラが自動的に codice_65 オブジェクトを解放する。Pythonの codice_66 文やRubyの codice_67 へのブロック引数も同様の効果がある。このような言語はいずれも標準の例外を定義し、それらがどのような状況で発生するかを定義している。ユーザーは独自の例外を発生させることもできる(実際、C++ と Python は任意の型の throw と catch が可能)。特定の codice_68 にマッチする codice_69 がない場合、マッチする codice_69 が見つかるまで入れ子構造を遡り、サブルーチン呼び出しを遡る。メインプログラムまで遡っても対応する codice_69 がない場合、プログラムは適切なエラーメッセージを出力して停止する。AppleScript スクリプト言語 は "codice_60" ブロックにいくつかの情報を提供する。ドナルド・クヌースは1974年の論文 "Structured Programming with go to Statements" でそれまでの制御構造でカバーされていない2種類の状況を提示し、それを実現する制御構造を例示した。他にも以下に示すような提案がある。これは1972年にダールが提案した。もし "xxx1" が省略されたら、テストが先頭にあるループとなる。もし "xxx2" が省略されたら、テストが最後尾にあるループとなる。"while" が省略されれば無限ループとなる。このように、このひとつの制御構文で多くのプログラミング言語にある複数の制御構文の代替となる。ありうべき派生としてループ内に複数の while テストを配置することを許すことが考えられるが、その場合は後述の exitwhen の方が適切である。このような構文を持たない言語でも一般に、任意のループ構造と、条件分岐とbreakを組み合わせて、同様のプログラムを書ける。Adaでは、上記のループ構造(loop-while-repeat)の代替として標準の無限ループ(loop-end loop)内で exit when節を使うことで同様の制御構造を実現できる(後述の exitwhen 文と混同しないよう注意されたい)。ループの命名(この例では "Read_Data")は必須ではないが、ループの入れ子で外側のループまで脱出させることができる。これは 1974年、Zahn が提案した。ここではそれを若干修正したものを示す。exitwhen は "xxx" 内で発生しうるイベントを指定するのに使い、イベントはイベント名を文として使用すると発生する。イベントが発生すると対応するアクションが実行され、その後 endexit 後の処理に移る。この制御構造はある状況を識別する部分と、その状況でとるべきアクションを明確に区別することができる。exitwhen は C++ 言語の try/catch 構造と概念的によく似ているが、サブルーチン呼び出しを超えたり任意の値を渡したりしないので、より効率的と思われる。また、コンパイラは指定されたイベントが全て発生する可能性があり、それらにアクションが対応しているかどうかをチェックできる。以下の単純な例は2次元配列から特定の要素を取り出すものである。Datamation誌(1973年12月)に掲載された記事で、R. Lawrence Clark は COME FROM 文を提案し、面白い例をいくつか提示した。それ自体は「GO TO論争に寄与する」と称したジョークであるが、ジャーゴンファイルの記事が指摘しているように、たとえばFortranのDO文は「そこで指定した行番号のある行からそこに飛ぶ」という一種のCOMEFROMであることなど、制御構文の問題に面白い視点を与えるものではある。setjmp/longjmpと関連させた指摘もある。COMEFROM文はINTERCALという難解プログラミング言語に実装された(INTERCALの実装者はジャーゴンファイルの編集者でもある)。

出典:wikipedia

LINEスタンプ制作に興味がある場合は、
下記よりスタンプファクトリーのホームページをご覧ください。