コンテンツにスキップ

英文维基 | 中文维基 | 日文维基 | 草榴社区

「手続き型プログラミング」の版間の差分

出典: フリー百科事典『ウィキペディア(Wikipedia)』
削除された内容 追加された内容
オブジェクト指向および関数型プログラミングに関して追記。
 
(17人の利用者による、間の52版が非表示)
1行目: 1行目:
{{複数の問題
{{独自研究|date=2016年6月}}
{{出典の明記|date=2016年6月}}
| 独自研究 = 2016年6月
| 出典の明記 = 2016年6月
[[ファイル:Процесс решения задачи проектирования.png|境界|右|フレームなし|265x265px]]
}}
{{プログラミング・パラダイム}}
'''手続き型プログラミング'''(てつづきがたプログラミング、{{lang-en-short|''Procedural programming''}})は、手続き(''procedure'')の定義と呼び出しをプログラムを組み立てる基礎にした[[プログラミングパラダイム]]である。手続きは言語によってサブルーチン、関数、サブプログラムとも呼ばれており、プログラムの命令コードのまとまりを任意の手続き名で抽象化したコードユニットである。手続きは入力されたパラメータ引数によって処理内容を変化させ、処理結果となるリターン値を出力する事ができる。パラメータ引数、リターン値、その双方を持たない手続きも定義できる。


{{プログラミング言語|index=てつつきかたふろくらみんく}}
手続きは一般的に[[非決定性有限オートマトン]]に準拠しているので、入力値による処理内容とそこからの出力値は、その手続き枠外の外部環境状態によっても変化する。この遷移図の可変性を指して手続き的(''procedual'')とも言われる。1958年の[[FORTRAN|FORTRANⅡ]]、[[ALGOL]]、[[COBOL]]といった最も初期の[[高水準言語]]から導入されている。
[[ファイル:Процесс решения задачи проектирования.png|境界|右|フレームなし|200x200ピクセル]]
'''手続き型プログラミング'''(てつづきがたプログラミング、{{lang-en-short|procedural programming}})は、コンピュータが実行すべき命令や手続き([[プロシージャ]])を順に記述していくことでプログラムを構成するスタイルの[[プログラミングパラダイム]]である<ref name="e-words">[https://e-words.jp/w/%E6%89%8B%E7%B6%9A%E3%81%8D%E5%9E%8B%E8%A8%80%E8%AA%9E.html 手続き型言語(命令型言語)とは - 意味をわかりやすく - IT用語辞典 e-Words]</ref>。この「手続き」とは分類便宜上の用語であり、[[プログラミング言語]]によっては[[サブルーチン]]、[[関数 (プログラミング)|関数]]、[[メソッド (計算機科学)|メソッド]]とも呼ばれているが、手続き型パラダイムの観点からは概ね同一視される。手続きはプログラム全体を区画した部分プログラムでもあり、一定量の計算ステップまたは命令コードのまとまりを、任意の定義名に結び付けて識別化したコードユニットである。手続き型プログラミングは[[命令型プログラミング]]の分類に属しており、厳密には命令型の部分集合だが、同一視されることもある<ref>[https://learn.microsoft.com/ja-jp/dotnet/standard/linq/functional-vs-imperative-programming 関数型プログラミングと命令型プログラミング - LINQ to XML - .NET | Microsoft Learn]</ref><ref>[https://www.shoeisha.co.jp/book/article/detail/284 Pythonってどんな言語? 特徴と歴史を『独習Python』から解説|翔泳社の本]</ref>。手続きの定義と呼び出しの機能をサポートし、プログラム全体を組み立てる土台とする言語を手続き型言語と呼ぶ。1958年の[[FORTRAN|FORTRAN II]]、[[ALGOL]]、[[COBOL]]といった最も初期の[[高水準言語]]から導入されている。


手続き(''procedure'')は、リンケージや可視性にもよるが、プログラム内のあらゆるポイントから呼び出す(''call'')ことが可能であり、手続き内の命令コード行の終端またはリターン命令に達したときは、その手続きを呼び出したポイントの次のアドレスに制御が移される。これは復帰(''return'')と呼ばれる動作である。通例、[[高水準言語]]の[[return文]]は[[機械語]]のリターン命令に対応しており、手続き内の途中位置からでも復帰できる。手続きは他の手続き内からの呼び出しの他、自身内からの呼び出しも可能であり、これを[[再帰|再帰呼び出し]](''recursive call'')という。手続きの呼び出しと復帰は、コンピュータ側が提供する[[コールスタック]]または[[スタックフレーム]]機能の命令アドレス管理によって実現されている。
== 特徴 ==
手続き型プログラミングでは基本的に、起点になるメインルーチンをルートにして階層的に分割された無数の手続きと、全ての手続きからアクセス可能なグローバル変数集合といったプログラム構成になる。複数の手続きからアクセスされるあらゆるデータを、グローバルにまとめてしまう簡素な設計は、プログラム全体への理解をむしろ促進するものとして小中規模のソフトウェア開発には適したものとされている。


=== 手続きとは ===
== 手続きの来歴 ==
手続き(''procedure'')の考え方は、[[機械語]]コードの時代から存在しており、[[低水準言語]]([[アセンブリ言語]])にある[[ニーモニック・コード|ニーモニックコード]]のCALL命令とRET命令が原点である。PUSH命令による引数の[[スタック|スタックメモリ]]への積み込みと、スタックポインタレジスタの減算によるローカル変数領域の確保、ベースポインタレジスタによる引数とローカル変数へのアクセスといった[[スタックフレーム]]の機能もアセンブリ言語由来のものである。CALL命令のジャンプ先アドレスに付けられたラベルは手続き名と同義になった。その仕組みは1950年代半ばから登場した[[高水準言語]]にもそのまま受け継がれた。ラベルは形式化された引数欄付きの呼び出し名となり、スタックフレーム処理も自動化隠蔽され、ソースコード上で明確に区分けされた手続き(プロシージャ)として誕生した。
[[手続き]](''procedure'')は、命令コード(''instruction code'')のまとまりをパラメータリスト付きの識別子に結び付けたコードユニットである。識別子は同時にリターン値の代入対象になる。命令コードの一行単位はステートメント(''statement'')と呼ばれる。プログラム起点のメインルーチンは手続きとは見なされない。[[プロシージャ|手続き]]はプログラム内のあらゆるポイントから呼び出すことができる。手続き内の終端位置に達した時は、その呼び出しポイントの次の命令コードにリターンされる。手続き内の途中位置からでもリターンできる。手続きの呼び出しとリターンは、一般にコンピュータ側が提供する[[コールスタック]]機能によって実現されている。


== 手続きとモジュール性 ==
手続きの名称は言語によって異なっている。[[COBOL]]では副プログラムが手続き相当であり、副プログラムの命令コード記述部分を手続き部と呼んだ。[[FORTRAN]]ではリターン値を持たない方の手続きをサブルーチン、持つ方の手続きを関数と呼んだ。[[ALGOL]]ではどちらも手続きと呼び、[[C言語]]ではどちらも関数と呼んだ。[[Pascal]]ではリターン値を持たない方を手続き、持つ方を関数とした。[[BASIC]]ではリターン値無しをサブプログラム、有りを関数と呼んだ。なお、FORTRANのサブルーチン&関数はその内部サブルーチン&内部関数を複数定義可能でそれらをまとめたものを外部手続き(邦訳は外部副プログラム)と定義している。
大きく複雑なプログラムでは特に[[モジュール性]]が重要である。手続き型プログラミングでは、モジュールへの入力は構文的には「引数」であり、出力は「リターン値」である。


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


モジュール性の低い手続きも簡単なプログラムではよく使われる。その場合、実行環境内の多数の変数にアクセスし、他の手続きでも同様にそれらの変数にアクセスする。
=== 手続き型プログラムの構成 ===
手続き型プログラムは、メインルーチンをルートにして階層的に分割された無数の手続きと、全ての手続きからアクセス可能なグローバル変数集合によって構成される。プログラムのコードは手続き単位に分割される。プログラムのデータはグローバル変数とローカル変数に大別される。ローカル変数は各手続きの[[スコープ]]内に分散配置されてその手続き専用になる。グローバル変数スコープには、複数の手続きからアクセスされる様々な変数が雑然と置かれることになる。したがって特定のグローバル変数を特定の手続きグループ専用にするといった設計はプログラマの注意力に委ねられた。どの手続きから参照されており、またどの手続きから変更されているかの把握が難しいグローバル変数は、バグの温床になることが多かった。これはグローバル変数問題などと呼ばれて1960年代前半には指摘されるようになっていた。


単純で自己完結的で再利用可能なインタフェースであるため、手続きを使って多数の人間が書いたコードを組み合わせることが可能となり、[[ライブラリ]]なども作成できるようになった。
=== モジュール機能 ===
グローバル変数問題の解決のために、任意の「手続きグループ+変数グループ」をまとめて[[モジュール]]として定義できる機能が誕生した。この機能を中心にしたパラダイムは{{仮リンク|モジューラプログラミング|en|modular programming}}と呼ばれた。これは手続き型プログラミングの最も身近な発展形である。グローバルスコープとローカルスコープの間にモジュールスコープが追加され、モジュール内アクセス限定の手続きと変数を定義できるようになった。モジュール内の手続きと変数は、グローバル公開するものとモジュール内限定するものに分けられて、後者は他のモジュールからの不正アクセスを[[コンパイル時]]にチェックできるようになった。この機能は{{仮リンク|情報隠蔽|en|information hiding}}と呼ばれる。


== 他のプログラミングパラダイムとの対比 ==
また、モジュール内の手続きと[[構造体]]の実装部分を隠蔽して、定義部分だけをグローバル公開することもできる。この機能は{{仮リンク|Abstraction (computer science)|en|Abstraction (computer science)|label=抽象化}}と呼ばれる。[[C言語]]で例えると定義部分とは[[ヘッダファイル]]、実装部分とは[[ソースコード]]である。手続きの定義部分とは「返り値型+手続き名+引数リスト」の[[関数プロトタイプ]]を指す。[[構造体]]の定義部分とはタグ名を指す。抽象化された構造体は、同モジュール内の手続きのための引数と返り値用途限定になり、フィールドにはアクセスできないものとなる。このモジュールでの抽象化機能は、定義部分に当てはめる実装部分のモジュールを[[コンパイル時]]や[[リンク時]]に選択できるようにしている。


=== 構造化とは ===
=== 命令型プログラミング ===
{{節stub}}
手続き型プログラミングと[[構造化プログラミング]]は、同じテーマでよく用いられる言葉である。構造化プログラミングの定義はやや曖昧であるが、コード記述視点とプログラム設計視点の二つから解釈される。前者のコード記述視点では、順接・分岐・反復の三つの[[制御構造|制御構文]]を用いて[[goto文]]を極力用いないソースコード記述を重視したプログラミングスタイルになる。[[構造化定理]]がよく引き合いに出されて、それに[[サブルーチン]]による適切なプログラム分割が加えられることもある。


=== オブジェクト指向プログラミング ===
後者のプログラム設計視点では、プログラム全体の適切な[[モジュール]]分割を図り、各モジュールの[[凝集度]]およびモジュール間の[[結合度]]の適切な設定を重視したプログラミングパラダイムになる。プログラム全体をモジュールの組み合わせとそれらの連携で構築しようとする考え方である。このモジュールによるプログラムの構造化は、1970年前後から盛んに研究され始めて「structured design(SD)」「structured analysis(SA)」「[[ジャクソンの構造化プログラミング|Jackson structured programming]](JSP)」「[[構造化分析設計技法|structured analysis and design technique]](SADT)」「[[SSADM|structured systems analysis and design method]](SSADM)」「modern structured analysis」といった数々の[[ソフトウェア工学]]テクニックが発表されている。グレンフォード・マイヤーズ、[[エドワード・ヨードン]]、[[トム・デマルコ]]などが有名である。
手続き型プログラミングでは、[[データ構造]]は[[構造体]]あるいは[[レコード (計算機科学)|レコード]]によって適宜まとめられるが、コードはデータ構造が定義されたモジュールにまとめられ、その中では散在しがちである。[[C言語]]のように、明確なモジュール機能をサポートしない言語もある。一方[[オブジェクト指向プログラミング]]では、データとコードはそれぞれ関連性の高いものが[[オブジェクト (プログラミング)|オブジェクト]]にまとめられる。クラスベースのオブジェクト指向言語では、オブジェクトの設計図となるクラスに[[フィールド (計算機科学)|フィールド]](メンバー変数)および[[メソッド (計算機科学)|メソッド]](メンバー関数)として定義される。


{{独自研究範囲|date=2024-03|一般にオブジェクト指向プログラミングの方が理解しやすいと言われている。その理由として、オブジェクト指向が人間の精神モデルの認知手法に近いからだという説もあるが、心理学が人間の認知モデルを完全には明確化できていない現時点では非常に不確かである。蒸気機関が発明されたとき、人間の精神は蒸気機関と比較された。[[コンピュータ]]が発明されたとき、人間の精神はそれと比較された。オブジェクト指向プログラミングが発明されると、人間の精神はそれと比較されることになったのである。}}
=== 命令型プログラミング ===
手続き型プログラミングは[[命令型プログラミング]]の分類に属している。これが意味する主なプログラム上の枠組みは、(A)手続きはパラメータ無しでもよくリターン値がなくてもよい、(B)手続き内ではグローバル変数とローカル静的変数と外部環境データが自由に変更される、(C)手続きは渡されたパラメータ以外の外部データおよび外部環境状態の影響を無制限に受けるので同じパラメータに対する処理内容とそのリターン値は一定ではない、の三点になる。


多くの場合、関連性の強いものを積極的にまとめて明確に分類できるオブジェクト指向のほうが[[凝集度]]が高くなり、保守が容易であると考えられている。クラスベースのオブジェクト指向言語の場合、プログラムはクラス群の定義から構成されている。オブジェクト指向言語と一口に言っても、全てをオブジェクトとみなす純粋なオブジェクト指向言語は少ない。例として最初のオブジェクト指向言語 [[Smalltalk]] があるが、商業的に成功したとは言いがたい。広く使われている[[C++]]や[[Java]]に代表されるように、多くのオブジェクト指向言語は、手続き型プログラミングとオブジェクト指向を融合させたものである。そのようなマルチパラダイムのオブジェクト指向言語は、広義では手続き型言語の一種として分類されることも多い<ref name="e-words"/>。
また、手続き型プログラミングと命令型プログラミングは、[[オートマトン|オートマタ理論]]の[[非決定性有限オートマトン]]見地からの同じ意味で用いられてもいる。手続き的(''procedual'')の用法としてはこちらの方が標準である。この「手続き的」の意味も上述の(A)(B)(C)と同じである。


以下にオブジェクト指向と手続き型の言語要素を比較した表を示す。
== 歴史 ==
手続き(''procedure'')の考え方自体はコンピュータ黎明期の[[機械語]]コードの時代から存在している。手続きの実装方式は、アセンブラなどの[[低水準言語]]で用いられる[[ニーモニック・コード|ニーモニックコード]]のCALL命令とRET命令が原点である。PUSH命令による引数の[[スタック|スタックメモリ]]への積み込みと、スタックポインタレジスタの減算による自動変数領域の確保、ベースポインタレジスタによる引数と自動変数の参照といった[[スタックフレーム]]の機能もアセンブラ由来のものである。CALL命令のジャンプ先アドレスに付けられたラベルは手続き名と同義になった。その仕組みは1950年代半ばから登場した[[高水準言語]]にもそのまま受け継がれた。ラベルは形式化されたパラメータリスト付きのプロシージャネームになり、スタックフレーム処理も自動化され、命令コード行のかたまりはソースコード上で明確に区分けされたコードブロックとして形式化された。こうして手続き(プロシージャ)は低水準言語から高水準言語への移行期に言わばごく自然に誕生している。


なお、史上初の高水準言語として1954年に公開されたFORTRANは、CALL命令とRET命令を備えていなかったので手続きの形式も持っていなかった。初代FORTRANのプログラムはPROGRAMという定義文で括られた一つのメインルーチンで記述されていた。故にこれは非手続き型言語である。プログラム規模の急速な拡大ですぐにサブルーチン構造も必要になり、1958年に発表されたFORTRANⅡではCALL命令とRET命令が追加された。メインルーチンから分けられたサブルーチンは「External Procedure(外部手続き、邦訳は外部副プログラム)」と定義され、その呼び出しを多用するプログラム記述がなされるようになった。

== 他のパラダイムとの対比 ==
=== オブジェクト指向プログラミング ===
[[オブジェクト指向プログラミング|オブジェクト指向]]の発端の一つは[[グローバル変数]]問題の解決策としてであった。オブジェクト指向では変数と手続きを共に[[クラス (コンピュータ)|クラス]]に所属させて管理する。クラスとは変数と手続きをひとまとめにしてグループ化したものである。手続き型の[[モジュール化|モジュール]]に似ているが、前節で説明した「[[抽象化 (計算機科学)|抽象化]]」の実装スタイルに大きな違いがある。クラスの方では、直接アクセスを禁止した変数集合にそのアクセス専用の手続きを付随させるという形式で、より明確にデータの抽象化を表現している。これは[[カプセル化]]という用語で説明されている。手続きの抽象化では、定義部分から呼び出される実装部分の選択を[[コンパイラ|コンパイル]]時または[[リンケージエディタ|リンカ]]時だけではなく更に実行時にも分岐できるようにしている。これは[[多態性]]という用語で説明されており、[[継承 (プログラミング)|継承]]という構造の上で実装されている。
{| class="wikitable"
{| class="wikitable"
|-
|-
! オブジェクト指向
! 純粋なオブジェクト指向
! 手続き型
! 純粋な手続き型
|-
| [[メソッド (計算機科学)|メソッド]]
| [[サブルーチン|手続き(プロシージャ)]]
|-
| [[オブジェクト (プログラミング)|オブジェクト]]
| [[構造体|構造体(レコード)]]
|-
|-
| [[クラス (コンピュータ)|クラス]]
| [[クラス (コンピュータ)|クラス]]
| [[モジュール]]
| [[モジュール]]
|-
|-
| [[ンス]]
| [[メッセージ (コピュー)|メッセージ]]
| 手続き呼び出し
| [[構造体]]
|-
| [[メソッド (計算機科学)|メソッド]]
| [[サブルーチン|手続き]]
|-
| [[フィールド (計算機科学)|データメンバ]]
| [[変数]]
|}
|}


=== 関数型プログラミング ===
=== 関数型プログラミング ===
手続き型言語における「関数」は一般的に副作用(状態の変更)を伴うことが多く、数学の関数とは別物である。
手続き型ではデータへの作用(読込と書込)を[[ステートメント]]と呼ばれる命令コードの一行単位で順々に実行していく。それに対して[[関数型言語|関数型]]では各データを関数/演算子でそれぞれつなげた「[[式 (プログラミング)|式]]」として一意に表現し、その式を[[評価]]または簡約するという流れの中で命令コードを実行する。評価は計算と同義であり、計算された式は結果値として新たなデータに変わる。その新たなデータは[[束縛変数|変数]]に束縛されるなどして後続の式で用いられるといった繰り返しになる。関数は1個以上の引数を、1つの返り値に変換する機能であると同時にその返り値と同一視される存在である。すなわち関数=値であるので関数は他の関数に引数として渡すこともできるし、他の関数の返り値にすることもできる。この仕組みは[[高階関数]]と呼ばれ、関数型の代表的特徴とされている。

{| class="wikitable"
数学の関数のように副作用をもたない関数を組み合わせてプログラムを記述していく関数型プログラミングのスタイルは、従来の命令型・手続き型プログラミングと対比されることが多いが、[[Haskell]]のような純粋な関数型言語は少なく、[[Scala]]や[[F Sharp|F#]]のように関数型のスタイルを主軸としながらも必要に応じてオブジェクト指向や手続き型のスタイルをとることが可能となっているマルチパラダイム言語のほうが多い<ref>[https://learn.microsoft.com/ja-jp/dotnet/fsharp/language-reference/values/ 値 - F# | Microsoft Learn]</ref>。
|-
! 関数型
! 手続き型
|-
| [[式 (プログラミング)|式]]
| [[ステートメント]]
|-
| [[関数]]
| [[手続き]]
|-
| [[代数的データ型]]
| [[プリミティブ型|基本型]]と[[構造体]]
|-
| [[自由変数と束縛変数|束縛変数と自由変数]]
|[[変数]]
|}


=== 論理型プログラミング ===
=== 論理型プログラミング ===
{{節stub}}
[[論理型プログラミング|論理型]]は「AはZである」「AとBはZである」「AはBのZである」「AはBをZする」といった知識を定義する[[論理式 (数学)|論理式]]風の[[ステートメント]]群を記述し、それに対して「AはZであるか?」「Aと?はZであるか」といった質問を表わす[[論理式 (数学)|論理式]]をメインルーチンにして解を導き出すといった形式でプログラムを構築する。命令コードをシーケンシャルに順接実行する手続き型に対して、論理型では[[パターンマッチング]]による選択別実行が中心になっている。論理式には[[原子論理式]]としての[[入出力]]命令や[[アプリケーションプログラミングインタフェース|API]]命令も含めることが出来るので、解を導き出すという流れの中で手続き型と同様の命令コードを実行できる。
{| class="wikitable"
|-
! 論理型
! 手続き型
|-
| 単位節
| データ
|-
| 確定節
| 手続き
|-
|目標節
|メインルーチン
|-
| 原子論理式
| ステートメント
|-
| 単一化
| 手続き呼び出し
|-
|導出節
|リターン値
|}


== 代表的な手続き型言語 ==
== 代表的な手続き型言語 ==
手続き型と見なされるプログラミング言語は、手続き(プロシージャ)の概念を明確に持っていて、構文として定義している。
1970年代後半からほとんどの手続き型言語はマルチパラダイム化しており、その代表的な発展形は[[オブジェクト指向プログラミング|オブジェクト指向]]である。以下の言語一覧にも後年にオブジェクト指向を導入しているものがあるが、それまでの期間が比較的長かったものを手続き型に入れている。


典型例は[[ALGOL]]およびその派生言語である[[Pascal]]や[[C言語]]である。狭義では、オブジェクト指向に必要とされる機能を言語仕様レベルでサポートする言語は手続き型言語に含まれない。オブジェクト指向言語が標準化されて普及する前に登場した手続き型言語は、そのような機能をサポートしないものが多く、[[Visual Basic]]のように限定的なサポートにとどまっているものもある。
*[[FORTRAN|'''FORTRANⅡ''']] - 1958年、初の手続き型言語。その構文は[[低水準言語]]の特徴を少し残していた。

*[[ALGOL|'''ALGOL 58''']] - 1958年、構文をより高水準化した。続く'''ALGOL 60'''は[[制御構造|構造化文]]を導入した初の構造化言語である。
* [[Ada]](83以前)
* '''[[COBOL]]''' - 1959年、事務処理用言語。構文を人間の可読性に合わせてテキスト寄りにした。
* [[ALGOL]]
*'''[[PL/I]]''' - 1964年、上記三言語の特徴を合わせて[[IBM|IBM社]]が開発した当時の大規模言語。
* [[BASIC]]
* '''[[BASIC]]''' - 1964年、[[インタプリタ]]式のビギナー向け言語。教育向け。
* [[C言語]]
*'''[[Forth]]''' - 1970年、軽量インタプリタ式。[[逆ポーランド記法]]と[[スタック]]指向でコンピュータ資源を節約。
* [[ColdFusion]]
* '''[[Pascal]]''' - 1970年、構造化プログラミングの模範言語。教育向け。
* [[COBOL]]
*'''[[C言語]]''' - 1972年、最も広く受け入れられた手続き型言語。
* [[Forth]]
* '''[[Modula-2]]''' - 1978年、[[モジュール]]を導入した最初期のモジューラ言語。
* [[FORTRAN]]
*'''[[Ada]]''' - 1983年、[[米国国防総省|米国防総省]]が開発した[[マルチパラダイムプログラミング言語|マルチパラダイム]]手続き型言語。モジュール、[[ジェネリックプログラミング|ジェネリクス]]、[[並行計算]]など。
* [[Maple]]
*'''[[QuickBASIC]]''' - 1985年、構造化されたBASIC。[[MS-DOS]]用。
* [[Mathematica]]
*'''[[Oberon]]''' - 1987年、マルチパラダイム。[[抽象メソッド|抽象手続き]]をまとめたレコード(≒[[インタフェース (抽象型)|インターフェース]])が導入された。
* [[MATLAB]]
*'''[[Perl]]''' - 1987年、[[Webアプリケーション|WEBアプリケーション]]開発向け。
* [[Modula-2]]
*[[Microsoft Visual Basic|'''Visual Basic''']] - 1991年、[[Microsoft Windows|Windows]]アプリケーション開発用のビギナー向け言語。
* [[Oberon]] (Oberon-1、[[Oberon-2]])
* [[PHP (プログラミング言語)|'''PHP''']] - 1995年、WEBアプリケーション開発向け。
* [[Occam]]
*'''[[VBScript]]''' - 1996年、[[ActiveX]]などのWEBコンポーネント開発向け。
* [[MUMPS|M言語]]
* [[Pascal]]
* [[Perl]](バージョン4以前)
* [[Pike]]
* [[PL/I]]
* [[VBScript]]
* [[Visual Basic]]
* [[Visual Basic for Applications]]

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

* [[Ada]](95以降)
* [[C++]]
* [[C Sharp|C#]]
* [[D言語]]
* [[ECMAScript]]([[ActionScript]]、[[DMDScript]]、[[JavaScript]]、[[JScript]])
* [[Go (プログラミング言語)|Go]]
* [[Java]]
* [[Kotlin]]
* [[Object Pascal]]/[[Delphi]]
* [[Objective-C]]
* [[Perl]](バージョン5以降)
* [[PHP (プログラミング言語)|PHP]]
* [[Python]]
* [[Ruby]]
* [[Rust (プログラミング言語)|Rust]]
* [[Swift (プログラミング言語)|Swift]]
* [[Visual Basic .NET]]

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

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

== 脚注 ==
{{reflist}}


== 関連項目 ==
== 関連項目 ==
* [[非手続き型言語|非手続き型プログラミング]]
*[[プログラミングパラダイム]]
* [[プログラミング言語]]
* [[命令型プログラミング]]
** [[オブジェクト指向プログラミング]]
** [[構造化プログラミング]]
** [[非手続き型言語|非手続き型プログラミング]]

* [[宣言型プログラミング]]
**[[関数型言語|関数型プログラミング]]
**[[論理型言語|論理型プログラミング]]
**[[問い合わせ言語]]


== 外部リンク ==
== 外部リンク ==
* [http://dmoz.org/Computers/Programming/Languages/Procedural/ Open Directory: Programming: Languages: Procedural]
* [http://dmoz.org/Computers/Programming/Languages/Procedural/ Open Directory: Programming: Languages: Procedural]


{{DEFAULTSORT:てつつきかたふろくらみんく}}

{{プログラミング言語の関連項目}}
[[Category:プログラミングパラダイム]]

[[Category:プログラミングパラダイム|てつつきかたふろくらみんく]]

2024年4月6日 (土) 15:55時点における最新版

手続き型プログラミング(てつづきがたプログラミング、: 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)に展開される。つまり、内部的には手続きを呼び出しているだけである。

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

脚注

[編集]

関連項目

[編集]

外部リンク

[編集]