「手続き型プログラミング」の版間の差分
手続きの来歴 |
編集の要約なし |
||
(同じ利用者による、間の26版が非表示) | |||
1行目: | 1行目: | ||
{{独自研究|date=2016年6月}} |
{{独自研究|date=2016年6月}} |
||
{{出典の明記|date=2016年6月}} |
{{出典の明記|date=2016年6月}} |
||
{{プログラミング言語|index=てつつきかたふろくらみんく}} |
|||
[[ファイル:Процесс решения задачи проектирования.png|境界|右|フレームなし|200x200ピクセル]] |
[[ファイル:Процесс решения задачи проектирования.png|境界|右|フレームなし|200x200ピクセル]] |
||
{{Template:プログラミング・パラダイム}} |
|||
'''手続き型プログラミング'''(てつづきがたプログラミング、{{lang-en-short|''Procedural programming''}})は、手続きの定義と呼び出しをプログラム全体を組み立てる土台にした[[プログラミングパラダイム]]である。[[プロシージャ|手続き]]は言語によって[[サブルーチン]]、[[関数 (プログラミング)|関数]]、[[メソッド (計算機科学)|メソッド]]とも呼ばれている。手続きはプログラム全体を区画した部分プログラムでもあり、一定量の計算ステップまたは命令コードのまとまりを、任意の定義名に結び付けて識別化したコードユニットである。手続き型プログラミングは[[命令型プログラミング]]の分類に属しており、1958年の[[FORTRAN|FORTRANⅡ]]、[[ALGOL]]、[[COBOL]]といった最も初期の[[高水準言語]]から導入されている。 |
|||
'''手続き型プログラミング'''(てつづきがたプログラミング、{{lang-en-short|''Procedural programming''}})は、手続きの定義と呼び出しをプログラム全体を組み立てる土台にした[[プログラミングパラダイム]]である。手続きは言語によってサブルーチン、関数、サブプログラムとも呼ばれている。手続きはプログラム全体を区分けした部分プログラムでもあり、一定量の計算ステップまたは命令コードのまとまりを、任意の定義名に結び付けて識別化したコードユニットである。手続き型プログラミングは[[命令型プログラミング]]の分類に属しており、1958年の[[FORTRAN|FORTRANⅡ]]、[[ALGOL]]、[[COBOL]]といった最も初期の[[高水準言語]]から導入されている。 |
|||
手続き(''procedure'')は |
[[プロシージャ|手続き]](''procedure'')はプログラム内のあらゆるポイントから呼び出す(''call'')ことができる。手続き内の終端位置に達した時は、その呼び出しポイントの次の命令コードに復帰(''return'')される。途中位置からでも復帰できる。手続きの呼び出しと復帰はコンピュータ側が提供する[[コールスタック]]機能によって実現されている。 |
||
== |
== 特徴 == |
||
手続き型プログラミングでは基本的に、起点になるメインルーチンをルートにして階層的に分割された無数の手続きと、全ての手続きから参照可能なグローバル変数集合といったプログラム構成になる。複数の手続きから参照されるあらゆるデータをグローバルにまとめてしまう簡素な設計は、プログラム全体への理解をむしろ促進するものとして小中規模のソフトウェア開発には適したものとされている。 |
|||
手続き(''procedure'')の考え方は、[[機械語]]コードの時代から存在しており、[[低水準言語]]([[アセンブリ言語|アセンブラ]])にある[[ニーモニック・コード|ニーモニックコード]]のCALL命令とRET命令が原点である。PUSH命令による引数の[[スタック|スタックメモリ]]への積み込みと、スタックポインタレジスタの減算によるローカル変数領域の確保、ベースポインタレジスタによる引数とローカル変数へのアクセスといった[[スタックフレーム]]の機能もアセンブラ由来のものである。CALL命令のジャンプ先アドレスに付けられたラベルは手続き名と同義になった。その仕組みは1950年代半ばから登場した[[高水準言語]]にもそのまま受け継がれた。ラベルは形式化された引数欄付きの呼び出し名となり、スタックフレーム処理も自動化隠蔽され、ソースコード上で明確に区分けされた手続き(プロシージャ)として誕生した。 |
|||
== 手続きと |
=== 手続きとは === |
||
[[手続き]](''procedure'')は、命令コード(''instruction code'')のまとまりをパラメータリスト付きの識別子に結び付けたコードユニットである。プログラム起点のメインルーチンは手続きとは見なされない。識別子がリターン値の代入対象になる。これは全言語共通の見解である。[[COBOL]]では副プログラムが手続き相当であり、副プログラムの命令コード記述部分を手続き部と呼んだ。[[FORTRAN]]ではリターン値を持たない方の手続きをサブルーチン、持つ方の手続きを関数と呼んだ。[[ALGOL]]ではどちらも手続きと呼び、[[C言語]]ではどちらも関数と呼んだ。[[Pascal]]ではリターン値を持たない方を手続き、持つ方を関数とした。[[BASIC]]ではリターン値無しをサブプログラム、有りを関数と呼んだ。なお、FORTRANのサブルーチン&関数はその内部サブルーチン&内部関数を複数定義可能でそれらをまとめたものを外部手続き(邦訳は外部副プログラム)と定義している。 |
|||
大きく複雑なプログラムでは特に[[モジュール性]]が重要である。手続き型プログラミングでは、モジュールへの入力は構文的には「引数」であり、出力は「リターン値」である。 |
|||
手続きの識別子は手続き名と呼ばれる。パラメータリストには任意の個数の引数が列挙される。引数無しのケースもある。手続き名+パラメータリストの次にコードブロックが置かれる。コードブロックには1行以上の命令コードが列記される。命令コードは引数の使用によってプロセスの多相性を実現できる。コードブロックは[[レキシカルスコープ]]の範囲になり、そのスコープ専用のローカル変数を定義できる。ローカル変数は静的変数と自動変数に分かれる。静的変数はプログラム実行中を通して代入値が保持される。自動変数は[[スタックフレーム]]を利用したもので、手続き内へのエントリと共に自動確保され、リターンと共に自動消去されるものである。命令コード行の終端に達するとリターンする。リターンとはその手続きの呼び出しポイントの次の命令行に移動することである。プロセスの結果を示すリターン値は手続きの識別子を媒体にして呼び出し元に渡される。リターン命令で途中位置からのリターンもできる。スタックフレームの利用により手続きは[[再帰呼び出し]]も可能である。 |
|||
変数[[スコープ]]は手続きのモジュール性を高めるもう1つの技法である。手続き内の変数は他の手続きからアクセスできない(逆も成り立つ)し、同じ手続きの複数の呼び出しの間でもそれが保たれる。スコープを超えたアクセスには特別な許可が必要である。 |
|||
=== 手続き型プログラムの構造 === |
|||
モジュール性の低い手続きも簡単なプログラムではよく使われる。その場合、実行環境内の多数の変数にアクセスし、他の手続きでも同様にそれらの変数にアクセスする。 |
|||
手続き型プログラムは、メインルーチンをルートにして階層的に分割された無数の手続きと、全ての手続きから参照可能なグローバル変数集合によって構成される。プログラムのコードは手続き単位に分割される。プログラムのデータはまずグローバル変数とローカル変数に大別される。ローカル変数は各手続きの[[スコープ]]内に分散配置されてその手続き専用になる。グローバル変数スコープには、複数の手続きから参照される様々な変数が雑然と置かれることになる。手続き型言語では、特定のグローバル変数を特定の手続きグループ専用にするといった設計はプログラマの注意力に委ねられた。どの手続きから参照され、またどの手続きから変更されるかの把握が難しいグローバル変数はバグの温床になり得るものであった。 |
|||
その解決のために任意の手続きグループと変数グループをまとめて形式化された[[モジュール]]として定義できる機能が誕生し、それに準拠したパラダイムはモジューラルプログラミング(''modular programming'')と呼ばれた。これは手続き型プログラミングの最も身近な発展形である。[[Modula-2]]がその先例となった。グローバルスコープとローカルスコープの間にモジュールスコープが追加され、グローバル公開された手続きと変数以外はモジュールスコープ内に隠蔽する事ができた。この機能は「情報隠蔽」と呼ばれる。また手続きと構造体の実装部分を隠蔽して定義部分だけをグローバル公開する機能は「抽象化」と呼ばれた。手続きの定義部分とは、返値型+手続き名+パラメータリストであり[[関数プロトタイプ]]と同義である。構造体の定義部分とはタグ名を指す。抽象化された構造体は同じモジュールの手続きのための引数用途限定になり、フィールドには当然アクセスできない。 |
|||
単純で自己完結的で再利用可能なインタフェースであるため、手続きを使って多数の人間が書いたコードを組み合わせることが可能となり、[[ライブラリ]]なども作成できるようになった。 |
|||
手続き型プログラミングと[[構造化プログラミング]]は混同されやすいが、後者は順接、分岐、反復の三つの[[制御構造|制御構造文]]を多用するコーディングスタイルに焦点を当てたパラダイムである。分岐はIF文、反復はWHILE文やFOR文で表現される。GOTO文を安易に用いないことが推奨されている。手続きによるプログラムコードの階層分割は、構造化と同義でもあるのでパラダイム上の重複面は否定できない。[[エドガー・ダイクストラ]]が考案した元祖構造化プログラミングの方では、上述のモジュールが構造化に向けた階層分割単位になり、そのモジュールは抽象化と統合詳細化に分離されていた。抽象化は、抽象データ構造体と抽象ステートメントのセットと言われ、上述の手続きの定義部分と構造体の定義部分のセットに似たものと見なせる。統合詳細化は、手続きの実装部分と構造体の実装部分のセットに似たものと見なせる。 |
|||
== 他のプログラミングパラダイムとの対比 == |
|||
=== 命令型プログラミング === |
=== 命令型プログラミング === |
||
手続き型は[[命令型プログラミング]]の分類に属している。これが意味する主なプログラム上の枠組みは、手続きはパラメータ無しでもよくリターン値がなくてもよい、手続き内ではグローバル変数とローカル静的変数とその他のプログラム状態が自由に変更される、手続きは渡されたパラメータ以外の情報要素の影響を無制限に受けるので同じパラメータに対するリターン値は一定でない、の三点になる。 |
|||
== 歴史 == |
|||
=== オブジェクト指向プログラミング === |
|||
手続き(''procedure'')の考え方自体はコンピュータ黎明期の[[機械語]]コードの時代から存在している。手続きの実装方式は、アセンブラなどの[[低水準言語]]で用いられる[[ニーモニック・コード|ニーモニックコード]]のCALL命令とRET命令が原点である。PUSH命令による引数の[[スタック|スタックメモリ]]への積み込みと、スタックポインタレジスタの減算による自動変数領域の確保、ベースポインタレジスタによる引数と自動変数の参照といった[[スタックフレーム]]の機能もアセンブラ由来のものである。CALL命令のジャンプ先アドレスに付けられたラベルは手続き名と同義になった。その仕組みは1950年代半ばから登場した[[高水準言語]]にもそのまま受け継がれた。ラベルは形式化されたパラメータリスト付きのプロシージャネームになり、スタックフレーム処理も自動化され、命令コード行のかたまりはソースコード上で明確に区分けされたコードブロックとして形式化された。こうして手続き(プロシージャ)は低水準言語から高水準言語への移行期に言わばごく自然に誕生している。 |
|||
手続き型プログラミングでは、プログラミングは[[データ構造]]とルーチンの集合に分割される。一方[[オブジェクト指向プログラミング]]では、プログラミングは[[オブジェクト (プログラミング)|オブジェクト]]に分割される。 |
|||
一般にオブジェクト指向プログラミングの方が理解しやすいと言われている。その理由として、オブジェクト指向が人間の精神モデルの認知手法に近いからだという説もあるが、心理学が人間の認知モデルを完全には明確化できていない現時点では非常に不確かである。蒸気機関が発明されたとき、人間の精神は蒸気機関と比較された。[[コンピュータ]]が発明されたとき、人間の精神はそれと比較された。オブジェクト指向プログラミングが発明されると、人間の精神はそれと比較されることになったのである。 |
|||
なお、史上初の高水準言語として1954年に公開されたFORTRANは、CALL命令とRET命令を備えていなかったので手続きの形式も持っていなかった。初代FORTRANのプログラムはPROGRAMという定義文で括られた一つのメインルーチンで記述されていた。故にこれは非手続き型言語である。プログラム規模の急速な拡大ですぐにサブルーチン構造も必要になり、1958年に発表されたFORTRANⅡではCALL命令とRET命令が追加された。メインルーチンから分けられたサブルーチンは「''External Procedure''(外部手続き、邦訳は外部副プログラム)」と定義され、その呼び出しを多用するプログラム記述がなされるようになった。これが手続き型プログラミングの始まりである。 |
|||
多くの場合、オブジェクト指向の方がプログラムが小さくなり、保守が容易であると考えられている。プログラムはクラス群の定義から構成されている。オブジェクト指向言語と一口に言っても、全てをオブジェクトとみなす純粋なオブジェクト指向言語は少ない。例として最初のオブジェクト指向言語 [[Smalltalk]] があるが、商業的に成功したとは言いがたい。多くのオブジェクト指向言語は、手続き型プログラミングとオブジェクト指向を融合させたものである。 |
|||
オブジェクト指向と手続き型の重要な違いとして、オブジェクト指向では[[関係データベース]]にアクセスするにあたって、データモデルのクラス構造へのマッピングが必要である。 |
|||
以下にオブジェクト指向と手続き型の言語要素を比較した表を示す。 |
|||
== 他のパラダイムとの対比 == |
|||
=== オブジェクト指向プログラミング === |
|||
[[オブジェクト指向プログラミング|オブジェクト指向]]の原点の一つは[[グローバル変数]]問題の解決である。オブジェクト指向では変数と手続きを共に[[クラス (コンピュータ)|クラス]]に所属させて管理する。クラスは変数と手続きをひとまとめにしてグループ化したものである。手続き型の[[モジュール化|モジュール]]と似ているが、前節で説明した「抽象化」の実装スタイルに大きな違いがある。クラスの方では変数集合に手続きを直接所属させるという形でより明解にデータの抽象化を表現している。抽象化されたデータはそれ専用の手続きを通したアクセス限定になるからである。これは[[カプセル化]]という用語で説明されている。また手続きの抽象化では、定義部分から呼び出される実装部分の選択をコンパイル時だけではなく更に実行時にも分岐できるようにしている。これは[[多態性]]という用語で説明されており、[[継承 (プログラミング)|継承]]という構造の上で実装されている。 |
|||
{| class="wikitable" |
{| class="wikitable" |
||
|- |
|- |
||
! |
! オブジェクト指向 |
||
! |
! 手続き型 |
||
|- |
|||
| [[メソッド (計算機科学)|メソッド]] |
|||
| [[サブルーチン|手続き(プロシージャ)]] |
|||
|- |
|||
| [[オブジェクト (プログラミング)|オブジェクト]] |
|||
| [[構造体|構造体(レコード)]] |
|||
|- |
|- |
||
| [[クラス (コンピュータ)|クラス]] |
| [[クラス (コンピュータ)|クラス]] |
||
| [[モジュール]] |
| [[モジュール]] |
||
|- |
|- |
||
| [[ |
| [[インスタンス]] |
||
| [[構造体]] |
|||
| 手続き呼び出し |
|||
|- |
|||
| [[メソッド (計算機科学)|メソッド]] |
|||
| [[サブルーチン|手続き]] |
|||
|- |
|||
| [[フィールド (計算機科学)|データメンバ]] |
|||
| [[変数]] |
|||
|} |
|} |
||
=== 関数型プログラミング === |
=== 関数型プログラミング === |
||
手続き型ではデータへの作用(読込と書込)を[[ステートメント]]と呼ばれる命令コードの一行単位で順々に実行していく。それに対して[[関数型言語|関数型]]では各データを関数と演算子でそれぞれつなげた「[[式 (プログラミング)|式]]」として一意に表現し、その式を[[評価]]または簡約するという流れの中で命令コードを実行する。評価は計算と同義であり、計算された式は結果値として新たなデータに変わる。その新たなデータは[[束縛変数|変数]]に束縛されるなどして後続の式で用いられるといった繰り返しになる。関数は1個以上の引数を一つの返り値に変換する機能であり、その返り値=値と同一視される。すなわち関数=値であるので関数は他の関数に引数として渡すこともできるし、返り値として渡すこともできる。この仕組みは[[高階関数]]と呼ばれ、関数型の代表的スタイルでもある。 |
|||
{| class="wikitable" |
|||
|- |
|||
! 関数型 |
|||
! 手続き型 |
|||
|- |
|||
| [[式 (プログラミング)|式]] |
|||
| [[ステートメント]] |
|||
|- |
|||
| [[関数]] |
|||
| [[手続き]] |
|||
|- |
|||
| [[代数的データ型]] |
|||
| [[プリミティブ型|基本型]]と[[構造体]] |
|||
|- |
|||
| [[自由変数と束縛変数|束縛変数と自由変数]] |
|||
|[[変数]] |
|||
|} |
|||
=== 論理型プログラミング === |
=== 論理型プログラミング === |
||
[[論理型プログラミング|論理型]]は「AはZである」「AとBはZである」「AはBのZである」「AはBをZする」といった定義節(確定節)をサブルーチン集合として記述し、それに対して「AはZであるか?」「Aと?はZであるか」といった質問節(目標節)をメインルーチンにして解を導き出すといった形態でプログラムを構築する。定義節はリテラルと呼ばれる[[原子論理式]]で構成されるが、予めリテラルとして用意されている組み込みサブルーチン、代入文、比較文を用いることにより、解を導き出すという流れの中で手続き型と同様の命令文を実行できる。 |
|||
{| class="wikitable" |
|||
|- |
|||
! 論理型 |
|||
! 手続き型 |
|||
|- |
|||
| 節集合 |
|||
| モジュール |
|||
|- |
|||
| 確定節 |
|||
| 手続き |
|||
|- |
|||
| リテラル |
|||
| ステートメント |
|||
|- |
|||
| 単一化 |
|||
| 手続き呼び出し |
|||
|- |
|||
|導出節 |
|||
|リターン値 |
|||
|- |
|||
|目標節 |
|||
|メインルーチン |
|||
|} |
|||
== 代表的な手続き型言語 == |
== 代表的な手続き型言語 == |
||
手続き型プログラミングは、1958年公開の[[FORTRAN|FORTRANⅡ]]、[[COBOL]]、[[ALGOL]]といった最も初期の[[高水準言語]]から導入されている。1970年代後半からほとんどの手続き型言語はマルチパラダイム化した。その代表的な発展形は[[オブジェクト指向プログラミング|オブジェクト指向]]である。以下の言語一覧にも後年にオブジェクト指向を導入しているものがあるが、それまでの期間が比較的長かったものを手続き型に入れている。 |
|||
手続き型と見なされるプログラミング言語は、手続き(プロシージャ)の概念を明確に持っていて、構文として定義している。 |
|||
*[[FORTRAN|'''FORTRANⅡ''']] - 1958年、初の手続き型言語。その構文は[[低水準言語]]の特徴を少し残していた。 |
|||
典型例は[[ALGOL]]である。手続きが[[メソッド (計算機科学)|メソッド]]の形でしか出現しない言語は、手続き型というよりもオブジェクト指向と見なすのが一般的であり、以下のリストにはそのような言語は登場しない。例えば、[[C Sharp|C#]]や[[Java]]がそうだが、[[C++]]はメソッド以外の形態で手続きを記述可能なので、以下に挙げてある。 |
|||
*[[ALGOL|'''ALGOL 58''']] - 1958年、構文をより高水準化した。続く'''ALGOL 60'''は[[制御構造|構造化文]]を導入した初の構造化言語である。 |
|||
* '''[[COBOL]]''' - 1959年、事務処理用言語。構文を人間の可読性に合わせてテキスト寄りにした。 |
|||
* [[Ada]] |
|||
*'''[[PL/I]]''' - 1964年、上記三言語の特徴を合わせて[[IBM|IBM社]]が開発した当時の大規模言語。 |
|||
* [[ALGOL]] |
|||
* '''[[BASIC]]''' - 1964年、[[インタプリタ]]式のビギナー向け言語。教育向け。 |
|||
* [[BASIC]] |
|||
*'''[[Forth]]''' - 1970年、軽量インタプリタ式。[[逆ポーランド記法]]と[[スタック]]指向でコンピュータ資源を節約。 |
|||
* [[C言語]] |
|||
* '''[[Pascal]]''' - 1970年、構造化プログラミングの模範言語。教育向け。 |
|||
* [[C++]] |
|||
*'''[[C言語]]''' - 1972年、最も広く受け入れられた手続き型言語。 |
|||
* [[ColdFusion]] |
|||
* '''[[Modula-2]]''' - 1978年、[[モジュール]]を導入した初のモジュール化言語。 |
|||
* [[COBOL]] |
|||
*'''[[Ada]]''' - 1983年、[[米国国防総省|米国防総省]]が開発した[[マルチパラダイムプログラミング言語|マルチパラダイム]]手続き型言語。モジュール、[[ジェネリックプログラミング|ジェネリクス]]、[[並行計算]]など。 |
|||
* [[D言語]] |
|||
*'''[[QuickBASIC]]''' - 1985年、構造化されたBASIC。[[MS-DOS]]用。 |
|||
* [[Object Pascal|Delphi言語]] |
|||
*'''[[Oberon]]''' - 1987年、マルチパラダイム。[[抽象メソッド|抽象手続き]]をまとめたレコード([[インタフェース (抽象型)|インターフェース]]に類似)が導入された。 |
|||
* [[ECMAScript]] ([[ActionScript]]、[[DMDScript]]、[[JavaScript]]、[[JScript]]) |
|||
*'''[[Perl]]''' - 1987年、[[Webアプリケーション|WEBアプリケーション]]開発向け。 |
|||
* [[Forth]] |
|||
*[[Microsoft Visual Basic|'''Visual Basic''']] - 1991年、[[Microsoft Windows|Windows]]アプリケーション開発用のビギナー向け言語。 |
|||
* [[FORTRAN]] |
|||
* [[PHP (プログラミング言語)|'''PHP''']] - 1995年、WEBアプリケーション開発向け。 |
|||
* [[Maple]] |
|||
*'''[[VBScript]]''' - 1996年、[[ActiveX]]などのWEBコンポーネント開発向け。 |
|||
* [[Mathematica]] |
|||
* [[MATLAB]] |
|||
* [[Modula-2]] |
|||
* [[Oberon]] (Oberon-1、[[Oberon-2]]) |
|||
* [[Occam]] |
|||
* [[MUMPS|M言語]] |
|||
* [[Pascal]] |
|||
* [[Perl]] |
|||
* [[PHP (プログラミング言語)|PHP]] |
|||
* [[Pike]] |
|||
* [[Python]] |
|||
* [[PL/I]] |
|||
* [[VBScript]] |
|||
* [[Microsoft Visual Basic|Visual Basic]] |
|||
== 関連項目 == |
== 関連項目 == |
||
*[[プログラミング |
* [[非手続き型言語|非手続き型プログラミング]] |
||
* [[プログラミング言語]] |
|||
* [[命令型プログラミング]] |
|||
** [[オブジェクト指向プログラミング]] |
|||
** [[構造化プログラミング]] |
|||
** [[非手続き型言語|非手続き型プログラミング]] |
|||
* [[宣言型プログラミング]] |
|||
**[[関数型言語|関数型プログラミング]] |
|||
**[[論理型言語|論理型プログラミング]] |
|||
**[[問い合わせ言語]] |
|||
== 外部リンク == |
== 外部リンク == |
||
* [http://dmoz.org/Computers/Programming/Languages/Procedural/ Open Directory: Programming: Languages: Procedural] |
* [http://dmoz.org/Computers/Programming/Languages/Procedural/ Open Directory: Programming: Languages: Procedural] |
||
{{プログラミング言語の関連項目}} |
|||
[[Category:プログラミングパラダイム|てつつきかたふろくらみんく]] |
[[Category:プログラミングパラダイム|てつつきかたふろくらみんく]] |
2020年8月2日 (日) 14:35時点における版
この記事には独自研究が含まれているおそれがあります。 |
プログラミング・パラダイム |
---|
手続き型プログラミング(てつづきがたプログラミング、英: Procedural programming)は、手続きの定義と呼び出しをプログラム全体を組み立てる土台にしたプログラミングパラダイムである。手続きは言語によってサブルーチン、関数、サブプログラムとも呼ばれている。手続きはプログラム全体を区分けした部分プログラムでもあり、一定量の計算ステップまたは命令コードのまとまりを、任意の定義名に結び付けて識別化したコードユニットである。手続き型プログラミングは命令型プログラミングの分類に属しており、1958年のFORTRANⅡ、ALGOL、COBOLといった最も初期の高水準言語から導入されている。
手続き(procedure)はプログラム内のあらゆるポイントから呼び出す(call)ことができる。手続き内の終端位置に達した時は、その呼び出しポイントの次の命令コードに復帰(return)される。途中位置からでも復帰できる。手続きの呼び出しと復帰はコンピュータ側が提供するコールスタック機能によって実現されている。
特徴
手続き型プログラミングでは基本的に、起点になるメインルーチンをルートにして階層的に分割された無数の手続きと、全ての手続きから参照可能なグローバル変数集合といったプログラム構成になる。複数の手続きから参照されるあらゆるデータをグローバルにまとめてしまう簡素な設計は、プログラム全体への理解をむしろ促進するものとして小中規模のソフトウェア開発には適したものとされている。
手続きとは
手続き(procedure)は、命令コード(instruction code)のまとまりをパラメータリスト付きの識別子に結び付けたコードユニットである。プログラム起点のメインルーチンは手続きとは見なされない。識別子がリターン値の代入対象になる。これは全言語共通の見解である。COBOLでは副プログラムが手続き相当であり、副プログラムの命令コード記述部分を手続き部と呼んだ。FORTRANではリターン値を持たない方の手続きをサブルーチン、持つ方の手続きを関数と呼んだ。ALGOLではどちらも手続きと呼び、C言語ではどちらも関数と呼んだ。Pascalではリターン値を持たない方を手続き、持つ方を関数とした。BASICではリターン値無しをサブプログラム、有りを関数と呼んだ。なお、FORTRANのサブルーチン&関数はその内部サブルーチン&内部関数を複数定義可能でそれらをまとめたものを外部手続き(邦訳は外部副プログラム)と定義している。
手続きの識別子は手続き名と呼ばれる。パラメータリストには任意の個数の引数が列挙される。引数無しのケースもある。手続き名+パラメータリストの次にコードブロックが置かれる。コードブロックには1行以上の命令コードが列記される。命令コードは引数の使用によってプロセスの多相性を実現できる。コードブロックはレキシカルスコープの範囲になり、そのスコープ専用のローカル変数を定義できる。ローカル変数は静的変数と自動変数に分かれる。静的変数はプログラム実行中を通して代入値が保持される。自動変数はスタックフレームを利用したもので、手続き内へのエントリと共に自動確保され、リターンと共に自動消去されるものである。命令コード行の終端に達するとリターンする。リターンとはその手続きの呼び出しポイントの次の命令行に移動することである。プロセスの結果を示すリターン値は手続きの識別子を媒体にして呼び出し元に渡される。リターン命令で途中位置からのリターンもできる。スタックフレームの利用により手続きは再帰呼び出しも可能である。
手続き型プログラムの構造
手続き型プログラムは、メインルーチンをルートにして階層的に分割された無数の手続きと、全ての手続きから参照可能なグローバル変数集合によって構成される。プログラムのコードは手続き単位に分割される。プログラムのデータはまずグローバル変数とローカル変数に大別される。ローカル変数は各手続きのスコープ内に分散配置されてその手続き専用になる。グローバル変数スコープには、複数の手続きから参照される様々な変数が雑然と置かれることになる。手続き型言語では、特定のグローバル変数を特定の手続きグループ専用にするといった設計はプログラマの注意力に委ねられた。どの手続きから参照され、またどの手続きから変更されるかの把握が難しいグローバル変数はバグの温床になり得るものであった。
その解決のために任意の手続きグループと変数グループをまとめて形式化されたモジュールとして定義できる機能が誕生し、それに準拠したパラダイムはモジューラルプログラミング(modular programming)と呼ばれた。これは手続き型プログラミングの最も身近な発展形である。Modula-2がその先例となった。グローバルスコープとローカルスコープの間にモジュールスコープが追加され、グローバル公開された手続きと変数以外はモジュールスコープ内に隠蔽する事ができた。この機能は「情報隠蔽」と呼ばれる。また手続きと構造体の実装部分を隠蔽して定義部分だけをグローバル公開する機能は「抽象化」と呼ばれた。手続きの定義部分とは、返値型+手続き名+パラメータリストであり関数プロトタイプと同義である。構造体の定義部分とはタグ名を指す。抽象化された構造体は同じモジュールの手続きのための引数用途限定になり、フィールドには当然アクセスできない。
手続き型プログラミングと構造化プログラミングは混同されやすいが、後者は順接、分岐、反復の三つの制御構造文を多用するコーディングスタイルに焦点を当てたパラダイムである。分岐はIF文、反復はWHILE文やFOR文で表現される。GOTO文を安易に用いないことが推奨されている。手続きによるプログラムコードの階層分割は、構造化と同義でもあるのでパラダイム上の重複面は否定できない。エドガー・ダイクストラが考案した元祖構造化プログラミングの方では、上述のモジュールが構造化に向けた階層分割単位になり、そのモジュールは抽象化と統合詳細化に分離されていた。抽象化は、抽象データ構造体と抽象ステートメントのセットと言われ、上述の手続きの定義部分と構造体の定義部分のセットに似たものと見なせる。統合詳細化は、手続きの実装部分と構造体の実装部分のセットに似たものと見なせる。
命令型プログラミング
手続き型は命令型プログラミングの分類に属している。これが意味する主なプログラム上の枠組みは、手続きはパラメータ無しでもよくリターン値がなくてもよい、手続き内ではグローバル変数とローカル静的変数とその他のプログラム状態が自由に変更される、手続きは渡されたパラメータ以外の情報要素の影響を無制限に受けるので同じパラメータに対するリターン値は一定でない、の三点になる。
歴史
手続き(procedure)の考え方自体はコンピュータ黎明期の機械語コードの時代から存在している。手続きの実装方式は、アセンブラなどの低水準言語で用いられるニーモニックコードのCALL命令とRET命令が原点である。PUSH命令による引数のスタックメモリへの積み込みと、スタックポインタレジスタの減算による自動変数領域の確保、ベースポインタレジスタによる引数と自動変数の参照といったスタックフレームの機能もアセンブラ由来のものである。CALL命令のジャンプ先アドレスに付けられたラベルは手続き名と同義になった。その仕組みは1950年代半ばから登場した高水準言語にもそのまま受け継がれた。ラベルは形式化されたパラメータリスト付きのプロシージャネームになり、スタックフレーム処理も自動化され、命令コード行のかたまりはソースコード上で明確に区分けされたコードブロックとして形式化された。こうして手続き(プロシージャ)は低水準言語から高水準言語への移行期に言わばごく自然に誕生している。
なお、史上初の高水準言語として1954年に公開されたFORTRANは、CALL命令とRET命令を備えていなかったので手続きの形式も持っていなかった。初代FORTRANのプログラムはPROGRAMという定義文で括られた一つのメインルーチンで記述されていた。故にこれは非手続き型言語である。プログラム規模の急速な拡大ですぐにサブルーチン構造も必要になり、1958年に発表されたFORTRANⅡではCALL命令とRET命令が追加された。メインルーチンから分けられたサブルーチンは「External Procedure(外部手続き、邦訳は外部副プログラム)」と定義され、その呼び出しを多用するプログラム記述がなされるようになった。これが手続き型プログラミングの始まりである。
他のパラダイムとの対比
オブジェクト指向プログラミング
オブジェクト指向の原点の一つはグローバル変数問題の解決である。オブジェクト指向では変数と手続きを共にクラスに所属させて管理する。クラスは変数と手続きをひとまとめにしてグループ化したものである。手続き型のモジュールと似ているが、前節で説明した「抽象化」の実装スタイルに大きな違いがある。クラスの方では変数集合に手続きを直接所属させるという形でより明解にデータの抽象化を表現している。抽象化されたデータはそれ専用の手続きを通したアクセス限定になるからである。これはカプセル化という用語で説明されている。また手続きの抽象化では、定義部分から呼び出される実装部分の選択をコンパイル時だけではなく更に実行時にも分岐できるようにしている。これは多態性という用語で説明されており、継承という構造の上で実装されている。
オブジェクト指向 | 手続き型 |
---|---|
クラス | モジュール |
インスタンス | 構造体 |
メソッド | 手続き |
データメンバ | 変数 |
関数型プログラミング
手続き型ではデータへの作用(読込と書込)をステートメントと呼ばれる命令コードの一行単位で順々に実行していく。それに対して関数型では各データを関数と演算子でそれぞれつなげた「式」として一意に表現し、その式を評価または簡約するという流れの中で命令コードを実行する。評価は計算と同義であり、計算された式は結果値として新たなデータに変わる。その新たなデータは変数に束縛されるなどして後続の式で用いられるといった繰り返しになる。関数は1個以上の引数を一つの返り値に変換する機能であり、その返り値=値と同一視される。すなわち関数=値であるので関数は他の関数に引数として渡すこともできるし、返り値として渡すこともできる。この仕組みは高階関数と呼ばれ、関数型の代表的スタイルでもある。
関数型 | 手続き型 |
---|---|
式 | ステートメント |
関数 | 手続き |
代数的データ型 | 基本型と構造体 |
束縛変数と自由変数 | 変数 |
論理型プログラミング
論理型は「AはZである」「AとBはZである」「AはBのZである」「AはBをZする」といった定義節(確定節)をサブルーチン集合として記述し、それに対して「AはZであるか?」「Aと?はZであるか」といった質問節(目標節)をメインルーチンにして解を導き出すといった形態でプログラムを構築する。定義節はリテラルと呼ばれる原子論理式で構成されるが、予めリテラルとして用意されている組み込みサブルーチン、代入文、比較文を用いることにより、解を導き出すという流れの中で手続き型と同様の命令文を実行できる。
論理型 | 手続き型 |
---|---|
節集合 | モジュール |
確定節 | 手続き |
リテラル | ステートメント |
単一化 | 手続き呼び出し |
導出節 | リターン値 |
目標節 | メインルーチン |
代表的な手続き型言語
手続き型プログラミングは、1958年公開のFORTRANⅡ、COBOL、ALGOLといった最も初期の高水準言語から導入されている。1970年代後半からほとんどの手続き型言語はマルチパラダイム化した。その代表的な発展形はオブジェクト指向である。以下の言語一覧にも後年にオブジェクト指向を導入しているものがあるが、それまでの期間が比較的長かったものを手続き型に入れている。
- FORTRANⅡ - 1958年、初の手続き型言語。その構文は低水準言語の特徴を少し残していた。
- ALGOL 58 - 1958年、構文をより高水準化した。続くALGOL 60は構造化文を導入した初の構造化言語である。
- COBOL - 1959年、事務処理用言語。構文を人間の可読性に合わせてテキスト寄りにした。
- PL/I - 1964年、上記三言語の特徴を合わせてIBM社が開発した当時の大規模言語。
- BASIC - 1964年、インタプリタ式のビギナー向け言語。教育向け。
- Forth - 1970年、軽量インタプリタ式。逆ポーランド記法とスタック指向でコンピュータ資源を節約。
- Pascal - 1970年、構造化プログラミングの模範言語。教育向け。
- C言語 - 1972年、最も広く受け入れられた手続き型言語。
- Modula-2 - 1978年、モジュールを導入した初のモジュール化言語。
- Ada - 1983年、米国防総省が開発したマルチパラダイム手続き型言語。モジュール、ジェネリクス、並行計算など。
- QuickBASIC - 1985年、構造化されたBASIC。MS-DOS用。
- Oberon - 1987年、マルチパラダイム。抽象手続きをまとめたレコード(インターフェースに類似)が導入された。
- Perl - 1987年、WEBアプリケーション開発向け。
- Visual Basic - 1991年、Windowsアプリケーション開発用のビギナー向け言語。
- PHP - 1995年、WEBアプリケーション開発向け。
- VBScript - 1996年、ActiveXなどのWEBコンポーネント開発向け。
関連項目
外部リンク