手続き型プログラミング

手続きの定義と呼び出しによるプログラムの構造化を重視しているパラダイム
手続き型言語から転送)

カテゴリ / テンプレート

手続き型プログラミング(てつづきがたプログラミング、: procedural programming)は、コンピュータが実行すべき命令や手続き(プロシージャ)を順に記述していくことでプログラムを構成するスタイルのプログラミングパラダイムである[1]。この「手続き」とは分類便宜上の用語であり、プログラミング言語によってはサブルーチン関数メソッドとも呼ばれているが、手続き型パラダイムの観点からは概ね同一視される。手続きはプログラム全体を区画した部分プログラムでもあり、一定量の計算ステップまたは命令コードのまとまりを、任意の定義名に結び付けて識別化したコードユニットである。手続き型プログラミングは命令型プログラミングの分類に属しており、厳密には命令型の部分集合だが、同一視されることもある[2][3]。手続きの定義と呼び出しの機能をサポートし、プログラム全体を組み立てる土台とする言語を手続き型言語と呼ぶ。1958年のFORTRAN IIALGOLCOBOLといった最も初期の高水準言語から導入されている。

手続き(procedure)は、リンケージや可視性にもよるが、プログラム内のあらゆるポイントから呼び出す(call)ことが可能であり、手続き内の命令コード行の終端またはリターン命令に達したときは、その手続きを呼び出したポイントの次のアドレスに制御が移される。これは復帰(return)と呼ばれる動作である。通例、高水準言語return文機械語のリターン命令に対応しており、手続き内の途中位置からでも復帰できる。手続きは他の手続き内からの呼び出しの他、自身内からの呼び出しも可能であり、これを再帰呼び出しrecursive call)という。手続きの呼び出しと復帰は、コンピュータ側が提供するコールスタックまたはスタックフレーム機能の命令アドレス管理によって実現されている。

手続きの来歴 編集

手続き(procedure)の考え方は、機械語コードの時代から存在しており、低水準言語アセンブリ言語)にあるニーモニックコードのCALL命令とRET命令が原点である。PUSH命令による引数のスタックメモリへの積み込みと、スタックポインタレジスタの減算によるローカル変数領域の確保、ベースポインタレジスタによる引数とローカル変数へのアクセスといったスタックフレームの機能もアセンブリ言語由来のものである。CALL命令のジャンプ先アドレスに付けられたラベルは手続き名と同義になった。その仕組みは1950年代半ばから登場した高水準言語にもそのまま受け継がれた。ラベルは形式化された引数欄付きの呼び出し名となり、スタックフレーム処理も自動化隠蔽され、ソースコード上で明確に区分けされた手続き(プロシージャ)として誕生した。

手続きとモジュール性 編集

大きく複雑なプログラムでは特にモジュール性が重要である。手続き型プログラミングでは、モジュールへの入力は構文的には「引数」であり、出力は「リターン値」である。

変数スコープは手続きのモジュール性を高めるもう1つの技法である。手続き内の変数は他の手続きからアクセスできない(逆も成り立つ)し、同じ手続きの複数の呼び出しの間でもそれが保たれる。スコープを超えたアクセスには特別な許可が必要である。

モジュール性の低い手続きも簡単なプログラムではよく使われる。その場合、実行環境内の多数の変数にアクセスし、他の手続きでも同様にそれらの変数にアクセスする。

単純で自己完結的で再利用可能なインタフェースであるため、手続きを使って多数の人間が書いたコードを組み合わせることが可能となり、ライブラリなども作成できるようになった。

他のプログラミングパラダイムとの対比 編集

命令型プログラミング 編集

オブジェクト指向プログラミング 編集

手続き型プログラミングでは、データ構造構造体あるいはレコードによって適宜まとめられるが、コードはデータ構造が定義されたモジュールにまとめられ、その中では散在しがちである。C言語のように、明確なモジュール機能をサポートしない言語もある。一方オブジェクト指向プログラミングでは、データとコードはそれぞれ関連性の高いものがオブジェクトにまとめられる。クラスベースのオブジェクト指向言語では、オブジェクトの設計図となるクラスにフィールド(メンバー変数)およびメソッド(メンバー関数)として定義される。

一般にオブジェクト指向プログラミングの方が理解しやすいと言われている。その理由として、オブジェクト指向が人間の精神モデルの認知手法に近いからだという説もあるが、心理学が人間の認知モデルを完全には明確化できていない現時点では非常に不確かである。蒸気機関が発明されたとき、人間の精神は蒸気機関と比較された。コンピュータが発明されたとき、人間の精神はそれと比較された。オブジェクト指向プログラミングが発明されると、人間の精神はそれと比較されることになったのである。[独自研究?]

多くの場合、関連性の強いものを積極的にまとめて明確に分類できるオブジェクト指向のほうが凝集度が高くなり、保守が容易であると考えられている。クラスベースのオブジェクト指向言語の場合、プログラムはクラス群の定義から構成されている。オブジェクト指向言語と一口に言っても、全てをオブジェクトとみなす純粋なオブジェクト指向言語は少ない。例として最初のオブジェクト指向言語 Smalltalk があるが、商業的に成功したとは言いがたい。広く使われているC++Javaに代表されるように、多くのオブジェクト指向言語は、手続き型プログラミングとオブジェクト指向を融合させたものである。そのようなマルチパラダイムのオブジェクト指向言語は、広義では手続き型言語の一種として分類されることも多い[1]

以下にオブジェクト指向と手続き型の言語要素を比較した表を示す。

純粋なオブジェクト指向 純粋な手続き型
メソッド 手続き(プロシージャ)
オブジェクト 構造体(レコード)
クラス モジュール
メッセージ 手続き呼び出し

関数型プログラミング 編集

手続き型言語における「関数」は一般的に副作用(状態の変更)を伴うことが多く、数学の関数とは別物である。

数学の関数のように副作用をもたない関数を組み合わせてプログラムを記述していく関数型プログラミングのスタイルは、従来の命令型・手続き型プログラミングと対比されることが多いが、Haskellのような純粋な関数型言語は少なく、ScalaF#のように関数型のスタイルを主軸としながらも必要に応じてオブジェクト指向や手続き型のスタイルをとることが可能となっているマルチパラダイム言語のほうが多い[4]

論理型プログラミング 編集

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

手続き型と見なされるプログラミング言語は、手続き(プロシージャ)の概念を明確に持っていて、構文として定義している。

典型例はALGOLおよびその派生言語であるPascalC言語である。狭義では、オブジェクト指向に必要とされる機能を言語仕様レベルでサポートする言語は手続き型言語に含まれない。オブジェクト指向言語が標準化されて普及する前に登場した手続き型言語は、そのような機能をサポートしないものが多く、Visual Basicのように限定的なサポートにとどまっているものもある。

しかし、C言語から発展したC++のように、多くのオブジェクト指向言語は手続き型言語の性質も受け継いでいる。そのため、オブジェクト指向型の手続き型言語と分類される[1]。代表的なものとして以下が挙げられる。

なお、C++などはクラスに属さないトップレベル(名前空間スコープ)の関数(自由関数)を定義することもできる一方、JavaC#ではメソッドは必ず何らかのクラスに属する必要があるが、呼び出しの際にオブジェクトのインスタンス化を必要としないstaticメソッド(静的メソッドまたはクラスメソッドとも呼ばれる)を定義することで代用できるため、そのような違いは手続き型の分類の決め手にはならない。また、多くのオブジェクト指向言語では、オブジェクトに対してstaticでないメソッド(非静的メソッドまたはインスタンスメソッドとも呼ばれる)を呼び出す場合、someInstance.someMethod()という形の構文記法が採用されているが、これは言語処理系によってメソッドの隠れた第0引数にオブジェクトをthisとして暗黙的に渡す形SomeClass.someMethod(someInstance)に展開される。つまり、内部的には手続きを呼び出しているだけである。

上記のようなオブジェクト指向の手続き型言語は、さらにラムダ式のような関数型プログラミングの機能も部分的にサポートすることが一般的となっており、プログラミングパラダイムの分類はますます曖昧となっている。

脚注 編集

関連項目 編集

外部リンク 編集