手続き型プログラミング

出典: フリー百科事典『ウィキペディア(Wikipedia)』
手続き型言語から転送)
ナビゲーションに移動 検索に移動
Процесс решения задачи проектирования.png

手続き型プログラミング(てつづきがたプログラミング、: Procedural programming)は、手続きの呼び出しをプログラムを組み立てる基本にしたプログラミングパラダイムである。手続きは言語によってサブルーチン、関数、サブプログラムとも呼ばれている。プログラムの命令コードの一行単位はステートメントと呼ばれ、手続きは一行以上のステートメントをまとめて手続き名で抽象化したコードユニットである。手続きの呼び出しはステートメントの順次実行と同義になり、分岐と反復を挟みながら命令コードを順々に実行していくことが手続き型プログラミングの形式的定義である。1958年のFORTRANⅡALGOLCOBOLといった最も初期の高水準言語から導入されている。

特徴[編集]

手続き型プログラミングでは基本的に、起点になるメインルーチンをルートにして階層的に分割された無数の手続きと、全ての手続きから参照可能なグローバル変数集合といったプログラム構成になる。複数の手続きから参照されるあらゆるデータをグローバルにまとめてしまう簡素な設計は、プログラム全体への理解をむしろ促進するものとして小中規模のソフトウェア開発には適したものとされている。

手続きとは[編集]

手続きprocedure)は、命令コード(instruction code)のまとまりをパラメータリスト付きの識別子に結び付けたコードユニットである。識別子は同時にリターン値の代入対象になる。命令コードの一行単位はステートメント(statement)と呼ばれる。プログラム起点のメインルーチンは手続きとは見なされない。手続きはプログラム内のあらゆるポイントから呼び出すことができる。手続き内の終端位置に達した時は、その呼び出しポイントの次の命令コードにリターンされる。手続き内の途中位置からでもリターンできる。手続きの呼び出しとリターンは、一般にコンピュータ側が提供するコールスタック機能によって実現されている。

手続きの名称は言語によって異なっている。COBOLでは副プログラムが手続き相当であり、副プログラムの命令コード記述部分を手続き部と呼んだ。FORTRANではリターン値を持たない方の手続きをサブルーチン、持つ方の手続きを関数と呼んだ。ALGOLではどちらも手続きと呼び、C言語ではどちらも関数と呼んだ。Pascalではリターン値を持たない方を手続き、持つ方を関数とした。BASICではリターン値無しをサブプログラム、有りを関数と呼んだ。なお、FORTRANのサブルーチン&関数はその内部サブルーチン&内部関数を複数定義可能でそれらをまとめたものを外部手続き(邦訳は外部副プログラム)と定義している。

手続きの識別子は手続き名と呼ばれる。パラメータリストには任意の個数の引数が列挙される。引数無しのケースもある。手続き名+パラメータリストの次にコードブロックが置かれる。コードブロックには1行以上の命令コードが列記される。命令コードは引数の使用によってプロセスの多相性を実現できる。コードブロックはレキシカルスコープの範囲になり、そのスコープ専用のローカル変数を定義できる。ローカル変数は静的変数と自動変数に分かれる。静的変数はプログラム実行中を通して代入値が保持される。自動変数はスタックフレームを利用したもので、手続き内へのエントリと共に自動確保され、リターンと共に自動消去されるものである。命令コード行の終端に達するとリターンする。リターンとはその手続きの呼び出しポイントの次の命令行に移動することである。プロセスの結果を示すリターン値は手続きの識別子を媒体にして呼び出し元に渡される。リターン命令で途中位置からのリターンもできる。スタックフレームの利用により手続きは再帰呼び出しも可能である。

手続き型プログラムの構造[編集]

手続き型プログラムは、メインルーチンをルートにして階層的に分割された無数の手続きと、全ての手続きから参照可能なグローバル変数集合によって構成される。プログラムのコードは手続き単位に分割される。プログラムのデータはまずグローバル変数とローカル変数に大別される。ローカル変数は各手続きのスコープ内に分散配置されてその手続き専用になる。グローバル変数スコープには、複数の手続きから参照される様々な変数が雑然と置かれることになる。手続き型言語では、特定のグローバル変数を特定の手続きグループ専用にするといった設計はプログラマの注意力に委ねられた。どの手続きから参照され、またどの手続きから変更されるかの把握が難しいグローバル変数はバグの温床になり得るものであった。

その解決のために任意の手続きグループと変数グループをまとめて形式化されたモジュールとして定義できる機能が誕生し、それに準拠したパラダイムはモジューラプログラミング英語版と呼ばれた。これは手続き型プログラミングの最も身近な発展形である。グローバルスコープとローカルスコープの間にモジュールスコープが追加され、グローバル公開されない手続きと変数はモジュールスコープ内に隠蔽する事ができた。この機能は「情報隠蔽英語版」と呼ばれる。また手続きと構造体の実装部分を隠して定義部分だけをグローバル公開する機能は「抽象化」と呼ばれた。手続きの定義部分とは、返値型+手続き名+パラメータリストであり関数プロトタイプと同義である。構造体の定義部分とはタグ名を指す。抽象化された構造体は同じモジュールの手続きのための引数用途限定になり、フィールドには当然アクセスできない。モジューラプログラミングの抽象化はコンパイル時またはリンカ時に、定義部分に当てはめる実装部分を任意のモジュールから選択可能にするのが主な有用性になる。

手続き型プログラミングと構造化プログラミングは混同されやすいが、後者は順接、分岐、反復の三つの制御構造文を多用するコーディングスタイルに焦点を当てたパラダイムである。分岐は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であるか」といった質問節(目標節)をメインルーチンにして解を導き出すといった形態でプログラムを構築する。定義節はリテラルと呼ばれる原子論理式で構成されるが、コマンドとしての組み込み述語記号、論理的同値としての代入演算子、述語記号としての比較演算子を用いることにより、解を導き出すという流れの中で手続き型と同様の命令コードを実行できる。

論理型 手続き型
節集合 モジュール
確定節 手続き
リテラル ステートメント
単一化 手続き呼び出し
導出節 リターン値
目標節 メインルーチン

代表的な手続き型言語[編集]

1970年代後半からほとんどの手続き型言語はマルチパラダイム化しており、その代表的な発展形はオブジェクト指向である。以下の言語一覧にも後年にオブジェクト指向を導入しているものがあるが、それまでの期間が比較的長かったものを手続き型に入れている。

関連項目[編集]

外部リンク[編集]