web-dev-qa-db-ja.com

関数型、宣言型、および命令型プログラミング

関数型、宣言型、および命令型プログラミングという用語はどういう意味ですか?

459
curiouskitty

これらに対する明確で客観的な定義は実際にはありません。これがIがそれらを定義する方法です:

命令型 - 焦点は、コンピュータが何をするのか(C、C++、Javaなど)ではなく、コンピュータが取るべきステップにあります。

宣言 - 焦点は、コンピュータの動作方法ではなく、コンピュータの動作方法にあります(例:SQL)。

機能的 - 再帰に重点を置いている宣言型言語のサブセット

100
Jason Baker

命令型および宣言型は、反対の2つのプログラミングスタイルについて説明しています。命令型がもっと「これが私が欲しいものです、今、あなたはそれをどうやってやるかを考え出している」一方、命令型が伝統的な「ステップバイステップレシピ」アプローチです。

これら2つのアプローチは、同じ言語と同じプログラムを使用しても、プログラミングを通して発生します。一般的に宣言型のアプローチは、プログラマがバグの可能性を少なくしながら非常に多くの詳細を指定する必要がなくなるため、望ましいと考えられます。ステップを定義すると、各ステップを手動で指定するよりも物事の信頼性が高まることを期待できます。

一方、命令型アプローチでは、低レベルの制御が可能になります。これはプログラミングに対する「マイクロマネージャーアプローチ」です。そしてそれはプログラマーがより効率的な答えを与えるために問題についての知識を利用することを可能にします。そのため、プログラムの一部をより宣言的なスタイルで記述することは珍しくありませんが、スピードが重視される部分をより必須にすることは珍しくありません。

ご想像のとおり、プログラムの作成に使用する言語は、宣言型言語に影響を及ぼします。結果の説明を元にして何をするかを決めるための組み込みの "smarts"言語は、もっと宣言的になりますより宣言的な層を上に構築することができるようになる前に、プログラマが最初に命令型コードでその種の知能を追加する必要があるものよりもアプローチ。したがって、たとえば、prologのような言語は、答えを検索するプロセスが組み込まれているため、非常に宣言的と見なされます。

これまでのところ、私は機能的プログラミングについて触れていないことに気づくでしょう。それは意味がすぐに他の二つに関連していない用語だからです。最も単純な関数型プログラミングでは、関数を使うということです。特に、 "ファーストクラス値"として関数をサポートする言語を使用しているということです。つまり、関数を書くことができるだけでなく、関数を書く関数(...という関数を書く)を書くこともできます。関数。つまり、関数は文字列や数値のように柔軟で一般的です。

それで、機能的、命令的、宣言的が一緒に言及されることは奇妙に思えるかもしれません。その理由は、関数型プログラミングの考えを「極限まで」取った結果です。純粋な意味では、関数は数学からのものです。入力を取り、常に同じ出力を出す一種の「ブラックボックス」です。そしてそのような振る舞いは変化する変数を格納することを必要としません。つまり、純粋に数学的に影響を受ける関数型プログラミングのアイデアを実装することを目的としたプログラミング言語を設計する場合、(ある程度限定された、技術的な意味で)変化する可能性のある値のアイデアを拒否することになります。

変数の変更方法を制限すると、ほとんどの場合、変数の変更方法を記述している命令型プログラミングの大部分は、宣言型のプログラムを作成することをプログラマに強制することになります。それを行う!そのため、関数型プログラミング、特に関数型言語でのプログラミングでは、宣言型のコードが多くなる傾向があります。

要約すると、

  • 命令型と宣言型は、2つの相反するプログラミングスタイルです(これらのスタイルを奨励するプログラミング言語には同じ名前が使用されます)。

  • 関数型プログラミングは、関数が非常に重要になり、結果として値の変更がそれほど重要でなくなるプログラミングスタイルです。値の変更を指定する機能が制限されているため、より宣言的なスタイルになります。

そのため、「関数型プログラミング」は「宣言型」と表現されることがよくあります。

52
andrew cooke

手短に:

命令型言語は、コンピュータが順番に実行する一連の命令を指定します(これを実行してから実行します)。

A 宣言型言語は、どの入力からどの出力が生成されるべきかに関する一連の規則を宣言します(たとえば、Aがある場合、結果はBになります)。エンジンはこれらの規則を入力に適用して出力を出すでしょう。

A 関数型言語は、入力がどのように出力に変換されるかを定義する一連の数学的/論理的関数を宣言します。例えば。 f(y) = y * y。それは宣言型言語の一種です。

50
mdja

必須:どのように私たちの目標を達成するために

   Take the next customer from a list.
   If the customer lives in Spain, show their details.
   If there are more customers in the list, go to the beginning

宣言:達成したい

   Show customer details of every customer living in Spain
23
Arturo Herrero

命令型プログラミングは、プログラムが命令によって構成されていて、コンピュータで実行される操作を記述するプログラミングスタイルを意味します起こるでしょう

宣言的プログラミングはあなたのプログラムが問題または解決策のいずれかの記述であるプログラミングのあらゆるスタイルを意味します - しかし作業がどのように行われるかを明示的に述べないでください

関数型プログラミングは、関数と関数の関数を評価することによってプログラミングを行うことです。 itは宣言的プログラミングの一種ですが、宣言的プログラミングの唯一の種類ではありません

論理プログラミング(Prologなど)は、宣言型プログラミングのもう1つの形式です。論理ステートメントが正しいかどうか(または満たすことができるかどうか)を判断することによって計算を行います。プログラムは通常、一連の事実および規則、すなわち一連の命令ではなく説明である。

項書き換え(たとえばCASL)は、宣言型プログラミングのもう1つの形式です。代数項の記号変換が含まれます。論理プログラミングや関数型プログラミングとはまったく異なります。

21
Dafydd Rees

命令型 - 式は実行する一連の動作を記述します(連想)

宣言型 - 式はプログラムの振る舞いに寄与する宣言です(連想、可換、べき等、単調)

機能的 - 式はのみを効果として持ちます。意味論は等式推論をサポートする

13
dmbarbour

以前の回答を書いてから、以下に引用する宣言的プロパティの 新しい定義 を定式化しました。また、命令型プログラミングをデュアルプロパティとして定義しました。

この定義は簡潔でより一般的であるため、以前の回答で提供したものよりも優れています。しかし、プログラミングや生活全般に適用される不完全性定理の意味合いは、人間が心を包み込むのが難しいため、理解するのはより難しいかもしれません。

定義の引用された説明は、宣言型プログラミングでのpure関数型プログラミングの役割について説明しています。

以下の定義は二重であると主張しているため、すべてのエキゾチックなプログラミングは、宣言型と命令型の次の分類に適合します。

宣言的対命令的

宣言的なプロパティは奇妙で、鈍く、一般的で曖昧ではない技術的に正確な定義でキャプチャすることは困難です。これは、意図しない副作用を引き起こすことなくプログラムの意味(セマンティクス)を宣言できるという単純な概念だからです。意味の表現と意図しない効果の回避の間には固有の緊張があり、この緊張は実際にはプログラミングと私たちの宇宙の 不完全性定理 に由来します。

宣言型をwhat to doとして、命令型をhow to doとして定義することは、過度に単純化され、技術的に不正確であり、あいまいです。あいまいなケースは、「what」が、プログラムを出力するプログラム(コンパイラー)の「how」です。

明らかに、 言語チューリングを完全にする無制限の再帰 は、評価の構文構造だけでなく、意味論でも同様です(別名、操作上の意味論)。これは、論理的にはゲーデルの定理に類似した例です-「公理の完全なシステムも矛盾します」。その引用の矛盾した奇妙さを熟考してください!また、セマンティクスの表現に証明可能な限界がないことを示す例でもあるため、証明することはできません 2 プログラム(および同様にそのセマンティクス)がa.k.a. Halting theoremを停止すること.

不完全性定理は、熱力学の第二法則で述べられているように、「エントロピー(別名、独立した可能性の数)永遠に最大になります 」。プログラムのコーディングと設計は決して終わらない-それは生きている!-それは現実世界のニーズに対処しようとし、現実世界のセマンティクスは常に変化し、より多くの可能性に向かっているからだ。人間は決して新しいものの発見を止めません(プログラムのエラーを含む;-)。

Edgeを持たないこの奇妙な宇宙(この宇宙の「外側」はない)内で、前述の望ましい概念を正確かつ技術的にとらえるためには、説明されるまで正しく聞こえない、簡潔だが一見単純ではない定義が必要です深く。

定義:


宣言的プロパティは、特定のモジュラーセマンティックを表現できるステートメントの可能なセットが1つだけ存在する場所です。

命令型のプロパティは二重であり、構成ではセマンティクスが一貫していない、および/または一連のステートメントのバリエーションで表現できます。


この宣言の定義は、セマンティックスコープで明確にlocalです。つまり、モジュラーセマンティックは、globalスコープでインスタンス化および使用される場所と方法に関係なく、一貫した意味を維持する必要があります。したがって、各宣言型モジュラーセマンティクスは、可能性のある他のすべてと本質的に直交する必要があり、不可能な(不完全性定理による)globalアルゴリズムまたは一貫性を立証するためのモデルは、「 多ければ多いほど良いというわけではない 」カーネギーメロン大学のコンピューターサイエンス教授、スタンダードMLの設計者の1人であるロバート・ハーパーによる。

これらのモジュラー宣言型セマンティクスの例には、カテゴリ理論ファンクターが含まれます。 Applicative 、名義入力、名前空間、名前付きフィールド、w.r.t。動作レベルのセマンティクスから、純粋な関数型プログラミングまで。

したがって、適切に設計された宣言型言語は、表現できるものの一般性がいくらか失われますが、本質的な一貫性で表現できるものが得られますが、 より明確に意味を表す になります。

前述の定義の例は、スプレッドシートプログラムのセル内の数式のセットです。異なる列および行のセルに移動したときに同じ意味を与えるとは考えられません。つまり、セル識別子が変更されます。セル識別子は、意図した意味の一部であり、意図した意味に不必要ではありません。したがって、各スプレッドシートの結果は一意のw.r.tです。数式セットのセル識別子に。この場合の一貫したモジュラーセマンティクスは、セル式のpure関数の入力および出力としてセル識別子を使用することです(以下を参照)。

ハイパーテキストマークアップ言語(HTML)-静的Webページの言語-は、(少なくともHTML 5より前の)動的な動作を表現する機能を持たない高度な(ただし完全ではない)宣言型言語の例です。 HTMLはおそらく最も習得しやすい言語です。動的な動作のために、JavaScriptなどの命令型スクリプト言語は通常、HTMLと組み合わされました。 JavaScriptを使用しないHTMLは、各名義型(つまりタグ)が構文の規則内で構成された場合に一貫した意味を維持するため、宣言的な定義に適合します。

宣言の競合する定義は、セマンティックステートメントの commutative および idempotent プロパティです。つまり、意味を変更せずにステートメントを並べ替えたり複製したりできます。たとえば、名前がモジュラーw.r.tである場合、名前付きフィールドに値を割り当てるステートメントは、プログラムの意味を変更することなく、並べ替えて複製できます。暗黙の順序に。名前は時々注文を意味します。セル識別子には列と行の位置が含まれます。スプレッドシートで合計を移動すると意味が変わります。それ以外の場合、これらのプロパティには、セマンティクスのglobal一貫性が暗黙的に必要です。順序と複製はセマンティクスに固有であるため、ランダムに順序付けまたは複製しても一貫性を保つようにステートメントのセマンティクスを設計することは一般的に不可能です。たとえば、「Foo exists」(または構築)と「Foo does not exist」(および破壊)というステートメント。意図されたセマンティクスの固有のランダムな矛盾を考慮する場合、この定義は宣言的プロパティにとって十分に一般的であると受け入れます。本質的に、この定義は、意味論に直交する一貫性を作ろうとするため、つまり、意味論の宇宙が動的に無制限であり、global一貫性パラダイムで捕捉できないという事実を無視しようとするため、一般化された定義として空虚です。

(構造評価順序の)下位レベルの操作セマンティクスに可換およびべき等のプロパティを要求すると、操作セマンティクスが宣言的なローカライズされたモジュラーセマンティクスに変換されます。 純粋な関数型プログラミング(命令型ループの代わりに再帰を含む)。次に、実装の詳細の操作順序は、高レベルのセマンティクスの一貫性に影響しません(つまり、globalに広がります)。たとえば、すべての出力が計算されるまで、つまり純粋な関数に類似するまで、出力は入力にコピーされないため、スプレッドシートの式の評価の順序(および理論的には重複も)は重要ではありません。

C、Java、C++、C#、PHP、およびJavaScriptは特に宣言的ではありません。 Coputeの構文とPythonの構文は、より宣言的に 意図した結果に結合 、つまり、不要なものを排除する一貫性のある構文セマンティクスにより、コードを忘れた後でも容易に理解できます。 CoputeとHaskellは、操作上のセマンティクスの決定論を強制し、「 自分自身を繰り返さないでください 」(DRY)を奨励します。これは、純粋な機能的パラダイムのみを許可するためです。


2 プログラムのセマンティクスを証明できる場合でも、たとえばCoq言語では、これは タイピング で表されるセマンティクスに限定され、タイリングはプログラムのセマンティクスのすべてをキャプチャーすることはできません。チューリング完全ではない言語に対してもです。 HTML + CSSを使用すると、一貫性のない組み合わせを表現することができ、そのため未定義のセマンティクスがあります。

多くの説明では、命令型プログラミングにのみ構文的に順序付けられたステートメントがあると誤って主張しています。これを明確にしました 命令型プログラミングと関数型プログラミングの混乱 。たとえば、HTMLステートメントの順序は、その意味の一貫性を低下させません。


編集: コメントに続く をRobert Harperのブログに投稿しました:

関数型プログラミングでは...変数の変化の範囲は型です

機能的プログラミングと命令型プログラミングをどのように区別するかに応じて、命令型プログラムの「割り当て可能」も、その変動性に限界を置くタイプを持つことがあります。

私が現在関数型プログラミングで理解している唯一の非混乱の定義は、a)ファーストクラスのオブジェクトおよび型としての関数、b)ループよりも再帰の優先、および/またはc)純粋な関数-つまり、目的のセマンティクスに影響しない関数ですメモしたときのプログラムの(したがって、完全に純粋な関数型プログラミングは、操作上のセマンティクスの影響により、汎用の意味論的なセマンティクスには存在しません。メモリ割り当て)。

純関数のべき等性とは、変数の関数呼び出しをその値で置き換えることができることを意味しますが、これは通常、命令型手続きの引数には当てはまりません。純粋な関数は宣言的であるようですw.r.t.入力タイプと結果タイプの間の未構成状態への遷移。

しかし、純粋な関数の構成は、そのような一貫性を維持しません。なぜなら、純粋な関数型プログラミング言語で副作用(グローバルな状態)命令プロセスをモデル化できるからです。 HaskellのIOMonadに加えて、チューリングの完全な関数型プログラミング言語でこれを行うことを防ぐことはまったく不可能です。

私が wrote 2012年に あなたの最近のブログ のコメントの同様のコンセンサスに思われるように、その宣言的プログラミングは、意図されたセマンティクスが決して不透明ではないという概念を捉える試みです。不透明なセマンティクスの例は、順序への依存、操作上のセマンティクスレイヤーでの高レベルのセマンティクスの消去への依存(例: キャストは変換ではなく、ジェネリックの制限により高レベルのセマンティクスが制限されます )、変数値への依存プログラミング言語ではチェックできません(正しいことが証明されています)。

したがって、チューリング以外の完全な言語のみが宣言型であると結論付けました。

したがって、宣言型言語の明確で明確な属性の1つは、その出力が列挙可能な一連の生成規則に従うことが証明できるということです。たとえば、スクリプト化されていない(つまり、チューリング完全ではない)特定のHTMLプログラム(インタープリターの分岐方法の違いを無視する)の場合、その出力の変動は列挙可能です。またはもっと簡潔に言えば、HTMLプログラムはその可変性の純粋な機能です。表計算プログラムも同様に、その入力変数の純粋な関数です。

したがって、宣言型言語は nbounded recursion のアンチテーゼであると思われます。つまり、ゲーデルの2番目の不完全性定理によると、自己参照定理は証明できません。

Lesie Lamport wrote ユークリッドは、型と論理の一致(カリー-ハワード対応など)により、プログラミング言語のコンテキストで数学の証明に適用されたゲーデルの不完全性定理にどのように取り組んだかについてのおとぎ話です。

10

命令型プログラミング:「機械」に何かをする方法を教えると、結果としてあなたが何をしたいのかが起こるでしょう。

宣言型プログラミング:あなたが何をしたいのかを「マシン」に伝え、そしてそれをどのように実行するかをコンピュータに理解させる。

命令の例

function makeWidget(options) {
    const element = document.createElement('div');
    element.style.backgroundColor = options.bgColor;
    element.style.width = options.width;
    element.style.height = options.height;
    element.textContent = options.txt;

    return element;
}

宣言の例

function makeWidget(type, txt) {
    return new Element(type, txt);
}

注:違いは、簡潔さ、複雑さ、抽象化のいずれでもありません。述べたように、違いはhow vs whatです。

6
yondoo

今日、新たな焦点:古い分類が必要ですか?

命令型/宣言型/関数型の側面は、以前は一般的な言語を分類するのに適していましたが、最近では(Java、Python、Javascriptなどの)すべての「大きな言語」その主なもの以外の「他の焦点」で表現する(通常は必須)、並列処理、宣言関数、ラムダなどを表現するオプション(通常 frameworks )。

それで、この質問の良い変形は、 "今日のフレームワークを分類するのに良い面は何ですか?"...重要な面は、ラベルを付けることができるということです 「プログラミングスタイル」...

アルゴリズムとデータの融合に焦点を当てる

説明する良い例です。あなたができるように ウィキペディアでjQueryについて読んでください

セレクターエンジン(...)によって可能にされたjQueryコア機能のセット(DOM要素選択、トラバーサル、および操作)は、新しい「プログラミングスタイル」を作成し、アルゴリズムとDOMデータ構造を融合させました。

そのため、jQueryは「新しいプログラミングスタイル」に焦点を当てた最も良い(一般的な)例です。つまり、オブジェクト指向だけではなく、「です。 アルゴリズムとデータ構造 " jQueryはスプレッドシートのようにやや反応的ですが、「セル指向」ではなく、「DOMノード指向」です...メインスタイルの比較これに関連して

  1. 融合なし:すべての「大きな言語」において、すべての関数型/宣言型/命令型の式において、通常はデータとアルゴリズムの「融合なし」です。あるオブジェクト指向によって、それは 厳密な代数構造 の観点から見た融合です。

  2. いくつかの融合:すべての古典的な融合戦略は、今日ではパラダイムとしてそれを使ったいくつかのフレームワークを持っています... データフロー 、 - イベント駆動型プログラミング (または awk および XSLT のような古いドメイン固有の言語)...最近のスプレッドシートを使ったプログラミングのように、それらもの例です。 リアクティブプログラミングスタイル。

  3. 大きな融合:「jQueryスタイル」... jQueryはドメインに特化した言語です。融合アルゴリズムと DOM-data -tructure "。
    PS:XQueryのような他の「問い合わせ言語」、SQL(命令式オプションとしてPLを使う)もデータアルゴリズム融合の例ですが、です。他のシステムモジュールとの融合なしで... Springfind()-variantとSpecification句を使用する場合は、もう1つの良い融合例です。

4
Peter Krauss

あなたの分類法は間違っていると思います。命令型と宣言型があります。汎関数は単なる宣言型のサブタイプです。ところで、ウィキペディアは同じ事実を述べています。

3
Rorick

宣言的プログラミングは、入力と出力の間のタイムレスなロジックを擬似コードで表現することによるプログラミングです。次の例は宣言的になります。

def factorial(n):
  if n < 2:
    return 1
  else:
    return factorial(n-1)

output = factorial(argvec[0])

ここでは「階乗」と呼ばれる関係を定義し、出力と入力の間の関係をその関係として定義します。ここで明らかになるように、どんな構造化言語についても宣言的プログラミングをある程度拡張することができます。宣言型プログラミングの中心的な考え方は不変のデータです。変数に代入した場合、そのようにするのは一度だけで、二度と行われることはありません。他の、より厳密な定義は全く副作用がないかもしれないことを伴います、これらの言語は時々「純粋に宣言的」と呼ばれます。

命令型スタイルでも同じ結果になります。

a = 1
b = argvec[0]
while(b < 2):
  a * b--

output = a

この例では、入力と出力の間に不朽の静的な論理的関係は表現されていません。一方のメモリアドレスが目的の結果になるまで手動でメモリアドレスを変更しました。すべての言語が宣言的セマンティクスをある程度拡張できるが、すべてが命令型を許可するわけではなく、一部の「純粋に」宣言型言語は副作用と突然変異を完全に許可することは明らかであるはずです。

宣言的言語は、「やり方」とは対照的に「やらなければならないこと」を指定するとよく言われます。宣言的プログラムでは、入力から出力までを取得する方法を指定します。あなたが指定する関係は、効果的に計算可能(重要な用語、あなたがそれを知らないならそれを調べてください)でなければなりません。もう1つのアプローチはnondeterministicプログラミングです。これは、実装が試行錯誤に成功するまですべてのパスを使い果たす前に、結果が満たすべき条件を実際に指定するだけです。

純粋に宣言的な言語はHaskellとPure Prologを含みます。一方から他方へのスライド式スケールは、次のようになります。Pure Prolog、Haskell、OCaml、Scheme/LISP、Python、Javascript、C--、Perl、PHP、C++、Pascall、C、Fortran、アセンブリ

3
Zorf

ここに記載されている "タイプ"に関するいくつかの良い答え。

私はしばしば関数型プログラミングの群衆と関連し​​ているいくつかの追加の、より「エキゾチックな」概念を提出します。

  • Domain Specific Language or DSLプログラミング:当面の問題に対処するための新しい言語を作成する。
  • メタプログラミング:あなたのプログラムが他のプログラムを書くとき。
  • Evolutionary Programming:あなた自身が継続的に改善するか、あるいはより良い世代のサブプログラムを生成するシステムを構築します。
3
msmithgu

一言で言えば、プログラミングスタイルがHow(やるべきこと)の詳細を抽象化すること(すること)を強調するほど、そのスタイルは宣言的であると見なされます。命令型の場合は逆のことが言えます。関数型プログラミングは宣言型に関連付けられています。

2
jchadhowell