web-dev-qa-db-ja.com

宣言型プログラミングとは何ですか?

私はこの用語をいくつかの異なる文脈で放り投げています。それは何ですか?

179
Brian G

宣言型プログラミングとは、自分がやりたいことではなく、やりたいことを記述するような方法でコードを書くことです。方法は、コンパイラーに任されています。

宣言型プログラミング言語の例は、SQLおよびPrologです。

137

他の答えはすでに宣言型プログラミングが何であるかを説明する素晴らしい仕事をしているので、私はそれがなぜ役に立つかもしれないかのいくつかの例を提供するつもりです。

コンテキストの独立性

宣言型プログラムはcontext-independentです。最終目標が何であるかを宣言するだけで、その目標に到達するための中間ステップは宣言しないため、同じプログラムを異なるコンテキストで使用できます。これは命令型プログラムでは難しいのです。なぜなら、それらは多くの場合、コンテキスト(たとえば、隠された状態)に依存しているからです。

例としてyaccを取り上げます。それは別名パーサージェネレータです。コンパイラコンパイラ、言語の文法を記述するための外部宣言型DSL。これにより、その言語のパーサーを記述から自動的に生成できます。コンテキストが独立しているため、このような文法を使用してさまざまなことができます。

  • その文法のCパーサーを生成します(yaccの元の使用例)
  • その文法のC++パーサーを生成します
  • その文法のJavaパーサーを生成します(Jayを使用)
  • その文法のC#パーサーを生成します(GPPGを使用)
  • その文法のRubyパーサーを生成します(Raccを使用)
  • その文法のツリービジュアライゼーションを生成します(GraphVizを使用)
  • yaccソースファイル自体のきれいな印刷、派手な書式設定、構文の強調表示を行い、言語の構文仕様としてリファレンスマニュアルに含めるだけです。

などなど …

最適化

コンピューターはどのステップをどの順序で実行するかを規定していないため、プログラムをより自由に再配置でき、場合によってはいくつかのタスクを並行して実行することもできます。良い例は、SQLデータベースのクエリプランナーとクエリオプティマイザーです。ほとんどのSQLデータベースでは、クエリが実際に実行中か、クエリが求められた実行中かを表示できます。多くの場合、これらのクエリはお互いにnothingのように見えます。クエリプランナーは、夢にも思わないことを考慮に入れます。たとえば、ディスクプラッターの回転待ち時間、またはまったく異なるユーザー用のまったく異なるアプリケーションが、同様のクエリと実行中のテーブルを実行したという事実とにかく、あなたがロードを避けるために一生懸命働いたことは、とにかく既にメモリにあります。

ここには興味深いトレードオフがあります:機械は、命令型言語よりも何かを行うためにhowを理解するためにより懸命に働く必要がありますが、それがdoesアウト、それは最適化段階のためのはるかに多くの自由とはるかに多くの情報を持っています。

76
Jörg W Mittag

大まかに:

宣言型プログラミングは次の傾向があります:-

  • 宣言または宣言文のセット。それぞれが意味を持ち(多くの場合、問題の領域で)、独立して単独で理解される場合があります。

Imperative Programmingは次の傾向があります:-

  • コマンドのシーケンス。それぞれが何らかのアクションを実行します。しかし、問題のドメインで意味を持っている場合と持っていない場合があります。

結果として、命令型のスタイルは、システムが実際に行っていることのメカニズムを読者が理解するのに役立ちますが、解決しようとしている問題についてほとんど洞察を与えない場合があります。一方、宣言型のスタイルは、読者が問題の領域とシステムが問題の解決に向けて取っているアプローチを理解するのに役立ちますが、力学の問題についてはあまり有益ではありません。

実際のプログラム(ProLogやCなど、スペクトルの終わりを好む言語で書かれたものでも)は、作品のさまざまな複雑さとコミュニケーションのニーズを満たすために、両方のスタイルがさまざまなポイントでさまざまな程度で存在する傾向があります。 1つのスタイルは他のスタイルより優れていません。それらは異なる目的に役立つだけであり、人生の多くのものと同様に、節度が重要です。

21
William Payne

例を示します

CSS(HTMLページのスタイル設定に使用)で、画像要素の高さを100ピクセル、幅を100ピクセルにしたい場合は、次のように単純に「宣言」します。

#myImageId {
height: 100px;
width: 100px;
}

CSSは宣言的な「スタイルシート」言語と見なすことができます。

このCSSを読み取り、interpretsするブラウザーエンジンは、イメージを自由に高さや幅を自由に変更できます。異なるブラウザエンジン(IEのエンジン、Chromeのエンジンなど)は、このタスクを異なる方法で実装します。

もちろん、独自の実装は宣言型言語ではなく、アセンブリ、C、C++、Java、JavaScript、Pythonなどの手続き型言語で記述されています。そのコードは、ステップごとに実行される一連のステップです(関数呼び出しを含めることもできます)。ピクセル値を補間したり、画面にレンダリングしたりすることができます。

12
Niko Bellic

申し訳ありませんが、他の多くの答えには同意しません。宣言型プログラミングの定義に関するこの混乱した誤解を止めたいと思います。

定義

サブ式の参照透過性(RT)は、宣言プログラミング言語のonly required属性です 、共有されていない唯一の属性であるため命令型プログラミングで。

宣言型プログラミングの他の引用された属性は、このRTから派生しています。詳細な説明については、上記のハイパーリンクをクリックしてください。

スプレッドシートの例

2つの回答では、スプレッドシートプログラミングについて言及しました。スプレッドシートプログラミング(別名数式)が可変global状態にアクセスしない場合は、宣言型プログラミングです。これは、可変セルの値がmain()(プログラム全体)のモノリシックinputおよびoutputであるためです。新しい値は、各数式の実行後にセルに書き込まれないため、宣言プログラムの実行中は変更できません(スプレッドシート内のすべての数式の実行)。したがって、相互に関連して、式はこれらの可変セルを不変と見なします。 RT関数は、immutableグローバル状態(および mutablelocal状態にもアクセスできます) )。

したがって、プログラムの終了時に(main()からの出力として)セル内の値を変更する機能は、ルールのコンテキストでそれらの値を変更可能に保存しません。主な違いは、各スプレッドシートの数式が実行された後にセル値が更新されないため、数式の実行順序は重要ではないということです。セルの値は、すべての宣言式が実行された後に更新されます。

12

宣言型プログラミングは絵であり、命令型プログラミングは絵を描くための指示です。

コンピューターが目的の場所に到達するために必要な手順を説明するのではなく、「それが何であるかを伝える」場合は、宣言スタイルで記述しています。

XMLを使用してデータをマークアップする場合、宣言プログラミングを使用しているのは、「これは人であり、誕生日であり、あちらに住所がある」と言っているからです。

宣言型プログラミングと命令型プログラミングを組み合わせて効果を高める例:

  • Windows Presentation Foundationは、宣言型XML構文を使用して、ユーザーインターフェイスがどのように見えるか、およびコントロールと基になるデータ構造との関係(バインディング)を記述します。

  • 構造化された構成ファイルは、宣言構文(「キー=値」のペアのような単純な)を使用して、データの文字列または値の意​​味を識別します。

  • HTMLは、ドキュメント全体に対して各テキストの役割を説明するタグでテキストをマークアップします。

8
Chris Wenham

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

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

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

宣言的対命令的

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

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

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

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

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

定義:


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

命令型プロパティは二重であり、セマンティクスは構成の下で一貫性がなく、ステートメントのバリエーションによって表現できます。


この宣言の定義は、セマンティックスコープでは明確にlocalです。つまり、モジュラーセマンティックは、globalスコープでインスタンス化および使用される場所と方法に関係なく、一貫した意味を維持する必要があります。したがって、各宣言型モジュラーセマンティクスは、可能な限り他のすべてと本質的に直交する必要があり、不可能ではありません(不完全性定理による)global整合性を立証するアルゴリズムまたはモデルであり、これも「 More Is Always Always Better 」標準MLの設計者の1人であるカーネギーメロン大学のコンピューターサイエンス教授であるRobert Harper氏。

これらのモジュラー宣言型セマンティクスの例には、カテゴリ理論ファンクターが含まれます。 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コヒーレンスで捕捉できないという事実を無視しようとするため、一般化された定義として空虚ですパラダイム。

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

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に加えて、Turingの完全に純粋な関数型プログラミング言語では、これを防ぐことはまったく不可能です。

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

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

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

だから、宣言型言語は nbounded recursion のアンチテーゼであるように思えます。つまり、ゲーデルの2番目の不完全性定理による自己参照定理は証明できません。

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

6

excelページを想像してください。納税申告書を計算するための数式が入力された列。

すべてのロジックはセルで宣言されて行われ、計算の順序は手順ではなく式自体によって決定されます。

これが、宣言型プログラミングのすべてです。プログラムのフローではなく、問題空間と解決策を宣言します。

Prologは、私が使用した唯一の宣言型言語です。別の種類の考え方が必要ですが、典​​型的な手続き型プログラミング言語以外の何かにあなたをさらすだけなら、学ぶのは良いことです。

6
paan

この質問に 回答 を提供した2011年12月以降、宣言型プログラミングの理解を深めました。ここに私の現在の理解が続きます。

私の理解(研究)の長いバージョンはこのリンクで詳しく説明されています。以下に要約を提供します。

命令型プログラミングでは、可変状態が格納および読み取られるため、プログラム命令の順序付けや複製によって、プログラムの動作(セマンティクス)が変更される可能性があります(さらに、バグ、つまり意図しない動作が発生する可能性もあります)。

最も素朴で極端な意味(以前の答えで断言しました)では、宣言型プログラミング(DP)は保存されたすべての可変状態を回避しているため、プログラム命令の順序付けや重複はではなく、プログラムの動作(セマンティクス)を変更します。

ただし、このような極端な定義は、ほとんどすべてのプログラムに格納された可変状態が含まれるため、現実の世界ではあまり有用ではありません。 スプレッドシートの例 は、DPのこの極端な定義に準拠しています。これは、プログラムコード全体が、新しい状態が保存される前に、入力状態の静的コピー1つで実行されるためです。その後、状態が変更されると、これが繰り返されます。しかし、ほとんどの現実世界のプログラムは、このようなモノリシックな状態変化のモデルに限定することはできません。

DPのより便利な定義は、プログラミング命令の順序付けや複製によって不透明なセマンティクスが変更されないことです。言い換えれば、発生するセマンティクスに隠れたランダムな変更はありません。プログラム命令の順序や複製の変更は、プログラムの動作に対する意図的で透過的な変更のみを引き起こします。

次のステップは、DPでどのプログラミングモデルまたはパラダイムが役立つかについて話すことですが、それはここでの質問ではありません。

5

これは、what何をすべきかhowを記述する代わりに、何かを行うべきであるかを記述することに基づいたプログラミングの方法です。

言い換えれば、式で作られたアルゴリズムを書くのではなく、物事をどのようにしたいかをレイアウトするだけです。 2つの良い例は、HTMLとWPFです。

このウィキペディアの記事は良い概要です: http://en.wikipedia.org/wiki/Declarative_programming

5
Kevin Berridge

何かをする方法ではなく、あなたが望むものをコンピュータに説明する。

5
denonde

宣言型プログラミングは、宣言、つまり宣言文を使用したプログラミングです。宣言文には、命令文と区別する多くのプロパティがあります。特に、宣言は次のとおりです。

  • 可換(並べ替え可能)
  • 連想(再グループ化可能)
  • べき等(意味を変えずに繰り返すことができます)
  • 単調(宣言は情報を減算しません)

関連するポイントは、これらはすべて構造的特性であり、主題に直交するということです。宣言とは、「What vs. How」ではありません。 "how" "what"宣言は内容ではなく構造に関するものです。宣言型プログラミングは、コードを抽象化およびリファクタリングする方法、およびコードをサブプログラムにモジュール化する方法に大きな影響を与えますが、ドメインモデルにはあま​​り影響しません。

多くの場合、コンテキストを追加することで、命令型から宣言型に変換できます。例えば。 「左に曲がります。(...それを待ちます...)右に曲がります。」 「ボブは11:01にフーとバーの交差点で左に曲がります。ボブは11:06にバーとバズの交差点で右に曲がります。」後者の場合、文はべき等で可換であるのに対し、前者の場合、文の再配置または繰り返しはプログラムの意味を大きく変えることに注意してください。

monotonicに関して、宣言はconstraintsを追加して可能性。ただし、制約は引き続き情報を追加します(より正確には、制約は情報です)。時変宣言が必要な場合、これを明示的な時間セマンティクスでモデル化するのが一般的です-例えば「ボールは平らです」から「ボールは時間Tで平らです」。 2つの矛盾する宣言がある場合、矛盾した宣言システムがありますが、これはsoft制約(優先度、確率など)を導入することで解決される可能性がありますパラコンシステントなロジックを活用します。

5
dmbarbour

宣言型プログラミングとは、「マシンの運用モデルではなく、開発者のメンタルモデルに適合する言語でのプログラミングの行為」です。

宣言型プログラミングと命令型プログラミングの違いは、構造化されたデータの解析の問題によって明確に示されています。

命令型プログラムは、相互に再帰的な関数を使用して入力を消費し、データを生成します。宣言型プログラムは、データの構造を定義する文法を表現して、データを解析できるようにします。

これら2つのアプローチの違いは、宣言型プログラムがホスト言語よりも問題のメンタルモデルにより密接にマッピングされた新しい言語を作成することです。

3
dan_waterworth

奇妙に聞こえるかもしれませんが、宣言型システムのリストにExcel(または実際にはスプレッドシート)を追加します。この良い例が here です。

2
Lunatik

DPは表現するための方法なので、説明します

  • A 目標式、条件-私たちが探しているもの。 1つ、多分、または複数ありますか?
  • いくつかの既知の事実
  • 既知の事実を拡張するルール

...そして、通常はnificationアルゴリズムを使用して目標を見つける控除エンジンがあります。

1
epatel