私は、この用語がいくつかの異なる状況で投げ回されるのを聞き続けます。それは何ですか?
私は、この用語がいくつかの異なる状況で投げ回されるのを聞き続けます。それは何ですか?
回答:
宣言型プログラミングとは、やりたいことではなく、やりたいことを記述するような方法でコードを書くことです。その方法を理解するのはコンパイラに任されています。
宣言型プログラミング言語の例は、SQLやPrologです。
他の回答はすでに宣言型プログラミングが何であるかを説明する素晴らしい仕事をしているので、それがなぜ有用であるかもしれないかについていくつかの例を提供するつもりです。
宣言型プログラムはコンテキストに依存しません。彼らは最終的な目標が何であるかを宣言するだけで、その目標に到達するための中間ステップは宣言しないので、同じプログラムを異なるコンテキストで使用できます。命令型プログラムでは、コンテキストに依存することが多いため(隠し状態など)、これを行うのは困難です。
yacc
例として考えてみましょう。それは別名パーサージェネレータです。コンパイラーコンパイラー。言語の文法を記述するための外部宣言型DSLで、その言語のパーサーを記述から自動的に生成できます。コンテキストに依存しないため、このような文法でさまざまなことができます。
yacc
)などなど …
どのステップをどの順序で実行するかをコンピューターに指示しないため、プログラムをより自由に再配置でき、いくつかのタスクを並列で実行することもできます。良い例は、SQLデータベースのクエリプランナーとクエリオプティマイザーです。ほとんどのSQLデータベースでは、実際に実行しているクエリと、実行を要求したクエリを表示できます。多くの場合、これらのクエリは何も見えませんお互いのように。クエリプランナーは、あなたが夢にも思わなかったことを考慮に入れます。たとえば、ディスクプラッターの回転待ち時間、またはまったく異なるユーザー向けのまったく異なるアプリケーションが、類似のクエリを実行しているという事実と、参加して、ロードを回避するために一生懸命働いたことは、とにかくすでにメモリにあります。
興味深いトレードオフがここにあります:マシンが把握するのは難しい仕事に持っているか、それは命令型言語の場合と比べて何かをするが、それは時に行いフィギュアそれを、それが最適化のための多くの自由と多くの情報を持っていますステージ。
大まかに:
宣言型プログラミングは次の傾向があります:-
命令型プログラミングは次の傾向があります:-
その結果、命令型スタイルは、システムが実際に行っていることのメカニズムを読者が理解するのに役立ちますが、解決しようとしている問題に対する洞察をほとんど提供しない場合があります。一方、宣言型スタイルは、読者が問題領域と、システムが問題の解決に向けて取るアプローチを理解するのに役立ちますが、力学の問題についてはあまり情報を提供しません。
実際のプログラム(ProLogやCなどのスペクトルの終わりを支持する言語で作成されたものでも)は、作品のさまざまな複雑さと通信のニーズを満たすために、さまざまな時点でさまざまな程度で両方のスタイルを提示する傾向があります。1つのスタイルが他のスタイルより優れているわけではありません。それらは異なる目的に役立つだけであり、人生の多くのことと同様に、節度が鍵となります。
ここに例があります。
CSS(HTMLページのスタイル設定に使用)で、画像要素の高さを100ピクセル、幅を100ピクセルにしたい場合は、次のようにそのことを単に "宣言"します。
#myImageId {
height: 100px;
width: 100px;
}
CSSは宣言的な「スタイルシート」言語と考えることができます。
このCSS を読み取って解釈するブラウザエンジンは、自由に画像をこれほど高くて広く表示させることができます。ブラウザーエンジン(IEのエンジン、Chromeのエンジンなど)によって、このタスクの実装方法は異なります。
もちろん、それらのユニークな実装は宣言型言語ではなく、アセンブリ、C、C ++、Java、JavaScript、Pythonなどの手続き型言語で記述されています。そのコードは、段階的に実行される一連のステップです(関数呼び出しが含まれる場合があります)。ピクセル値を補間したり、画面にレンダリングしたりすることができます。
申し訳ありませんが、他の多くの回答には同意しないでください。宣言型プログラミングの定義に関するこの混乱した誤解を止めたいと思います。
定義
サブ式の参照透過性(RT)は、宣言型プログラミング式の唯一の必須属性です。これは、命令型プログラミングと共有されない唯一の属性だからです。
宣言型プログラミングの他の引用属性は、このRTから派生しています。詳細な説明については、上のハイパーリンクをクリックしてください。
スプレッドシートの例
2つの回答がスプレッドシートのプログラミングに言及しています。スプレッドシートプログラミング(別名、数式)が変更可能なグローバル状態にアクセスしない場合は、宣言型プログラミングです。これは、変更可能なセル値が(プログラム全体)のモノリシック入力および出力であるためmain()
です。新しい値は、各式が実行された後はセルに書き込まれないため、宣言型プログラム(スプレッドシート内のすべての式の実行)の実行中は変更できません。したがって、相互に関連して、式はこれらの可変セルを不変であると見なします。RT関数は、不変のグローバル状態(および変更可能なローカル状態)にアクセスできます。
したがって、プログラムの終了時に(からの出力としてmain()
)セルの値を変更する機能は、ルールのコンテキストでセルに値を変更可能にしません。主な違いは、各スプレッドシートの数式が実行された後はセル値が更新されないため、数式を実行する順序は重要ではありません。セルの値は、すべての宣言式が実行された後に更新されます。
宣言型プログラミングはその絵であり、命令型プログラミングはその絵を描くための指示です。
あなたがそれが何であるかをそれで伝えるなら、あなたが望む場所に到達するためにコンピュータがとるべきステップを説明するのではなく、あなたは宣言的なスタイルで書いています。
XMLを使用してデータをマークアップする場合、「これは人であり、誕生日であり、向こうには住所がある」と言っているため、宣言型プログラミングを使用しています。
宣言型プログラミングと命令型プログラミングを組み合わせて効果を高める例:
Windows Presentation Foundationは、宣言型XML構文を使用して、ユーザーインターフェイスの外観、およびコントロールと基になるデータ構造の間の関係(バインディング)を記述します。
構造化構成ファイルは、宣言的な構文(「key = value」のペアのように単純)を使用して、データの文字列または値の意味を識別します。
HTMLは、各テキストがドキュメント全体に対してどのような役割を持つかを説明するタグでテキストをマークアップします。
宣言型プログラミングとは、宣言、つまり宣言型の文を使用したプログラミングです。宣言文には、それらを命令文と区別するいくつかの特性があります。特に、宣言は次のとおりです。
関連する点は、これらはすべて構造的特性であり、主題と直交しているということです。宣言は「何をどのように」についてではありません。「what」を宣言するのと同じくらい簡単に、「how」を宣言(表現および制約)できます。宣言は、内容ではなく構造についてです。宣言型プログラミングは、コードを抽象化およびリファクタリングする方法、およびコードをサブプログラムにモジュール化する方法に大きな影響を与えますが、ドメインモデルにはそれほど影響しません。
多くの場合、コンテキストを追加することにより、命令型から宣言型に変換できます。例:「左折。(...お待ちください...)右折してください。」「ボブはフーとバーの交差点で11:01に左折します。ボブはバーとバズの交差点で11:06に右折します。」後者の場合、文はべき等で可換的ですが、前者の場合、文を並べ替えたり繰り返したりすると、プログラムの意味が大きく変わります。
単調性に関して、宣言は可能性を差し引く制約を加えることができます。しかし、制約は依然として情報を追加します(より正確には、制約は情報です)。時間的に変化する宣言が必要な場合は、これを明示的な時間的セマンティクスでモデル化するのが一般的です。たとえば、「ボールはフラット」から「ボールは時間Tでフラット」などです。2つの矛盾する宣言がある場合、一貫性のない宣言システムがありますが、これはソフト制約(優先度、確率など)を導入するか、パラコンシステントロジックを活用することで解決できる場合があります。
2011年12月にこの質問への回答を提供して以来、私は宣言型プログラミングの理解を深めました。ここに私の現在の理解があります。
私の理解(研究)の長いバージョンについては、このリンクで詳しく説明しています。このリンクを読んで、以下で提供する概要を深く理解してください。
命令型プログラミングでは、変更可能な状態が格納および読み取られるため、プログラム命令の順序付けや複製により、プログラムの動作(セマンティクス)が変更される可能性があります(バグ、つまり意図しない動作も発生します)。
最も素朴で極端な意味で(以前の回答で私が断言しました)、宣言型プログラミング(DP)は格納されたすべての変更可能な状態を回避しているため、プログラム命令の順序付けや複製はプログラムの動作(セマンティクス)を変更できません。
ただし、ほとんどすべてのプログラムに格納された変更可能な状態が含まれるため、このような極端な定義は現実の世界ではあまり役に立ちません。スプレッドシートの例 DPのこの極端な定義に準拠して、全体のプログラムコードは、新しい状態が格納される前に、入力された状態の一の静的コピーで完全に実行されるからです。その後、状態が変更されると、これが繰り返されます。しかし、ほとんどの実際のプログラムは、そのような状態変化のモノリシックモデルに限定することはできません。
DPのより有用な定義は、プログラミング命令の順序付けや複製が不透明なセマンティクスを変更しないことです。言い換えると、発生するセマンティクスにランダムな変更が隠されているわけではありません。プログラムの命令順序や重複の変更は、プログラムの動作に意図的で透過的な変更のみを引き起こします。
次のステップは、どのプログラミングモデルまたはパラダイムがDPに役立つかについて話すことですが、ここでは問題ではありません。
Functional programming
最近の流行語であり、本質的に宣言型プログラミングのサブセットです。C#言語のLINQは、言語自体が本質的に必須である場合の関数型プログラミングの要素です。そのため、C#はその定義に従うハイブリッドのようなものになります。
それは、それがどのように機能すべきかを説明するのではなく、何かが何であるか、または何であるかを説明することに基づいたプログラミング方法です。
言い換えれば、式で構成されるアルゴリズムを作成するのではなく、物事のあり方をレイアウトするだけです。2つの良い例がHTMLとWPFです。
このウィキペディアの記事は良い概要です:http : //en.wikipedia.org/wiki/Declarative_programming
以前の回答を書いてから、以下に引用する宣言的プロパティの新しい定義を作成しました。また、命令型プログラミングをデュアルプロパティとして定義しました。
この定義は簡潔であり、より一般的であるため、以前の回答で提供した定義よりも優れています。しかし、プログラミングや生活全般に当てはまる不完全性の定理の意味は人間が心を包み込むのが難しいため、理解するのはもっと難しいかもしれません。
引用されている定義の説明では、宣言型プログラミングで純粋関数型プログラミングが果たす役割について説明しています。
宣言型と命令型
宣言的なプロパティは奇妙で、鈍く、技術的に正確な定義で捉えることは困難であり、一般的で曖昧ではありません。これは、意図しない副作用を招くことなくプログラムの意味(別名セマンティクス)を宣言できるからです。意味の表現と意図しない影響の回避との間には固有の緊張があり、この緊張は実際にはプログラミングと宇宙の不完全性定理に由来しています。
これは、技術的に不正確な単純化、およびとして宣言型を定義することがしばしば曖昧である「何をすべきか」などと不可欠「どのように行います」。あいまいなケースは、プログラムを出力するプログラム(コンパイラ)の「何」が「どのように」であるかです。
明らかに、言語Turingを完全なものにする無制限の再帰は、評価の構文構造(別名操作セマンティクス)だけでなく、セマンティクスにも類似しています。これは論理的にはゲーデルの定理に類似した例です。「公理の完全なシステムもすべて矛盾しています」。その引用の矛盾した奇妙さを考えてください!したがって、我々は証明できない、また、意味論の発現が証明可能に拘束されていない方法を示す例である2を、そのプログラム(および同様にその意味論)の停止定理別名停止。
不完全性の定理は、熱力学の第2法則で述べられているように、「エントロピー(別名、独立した可能性の数)が永久に最大になる傾向がある」という宇宙の基本的な性質に由来します。プログラムのコーディングと設計は、現実世界のニーズに対処しようとするものであり、現実世界のセマンティクスは常に変化し、より多くの可能性に向かって流れているため、完成することはありません。人間は新しいもの(プログラムのエラーを含む;-)の発見を決して止めません。
エッジのないこの奇妙な宇宙の中で、この前述の望ましい概念を正確かつ技術的に捉えるには(つまり、宇宙の「外側」は存在しないことに注意してください)、簡潔でありながら一見単純ではない定義が必要です。深く。
定義:
宣言的プロパティは、特定の各モジュールセマンティクスを表現できるステートメントの可能なセットが1つだけ存在できる場所です。
必須プロパティ3は、セマンティクスは、組成物の下に矛盾するおよび/または文のセットのバリエーションで表現できる場合、デュアルあります。
この宣言の定義は、セマンティックスコープで明確にローカルです。つまり、グローバルスコープでインスタンス化および使用される場所と方法に関係なく、モジュールセマンティックが一貫した意味を維持する必要があります。したがって、各宣言型モジュラーセマンティックは、すべての可能なその他-及びません(原因不完全定理に)不可能に本質的に直交性であるべき世界も「のポイントで一貫性を目撃するためのアルゴリズムやモデル、その他のではありません常により良いロバート・ハーパー、教授によります」標準MLの設計者の1人であるカーネギーメロン大学でコンピュータサイエンスを専攻しました。
これらのモジュール式の宣言的意味論の例としては、例えば、カテゴリ論ファンクタ含まれ、名目上のタイピングを、名前空間、名前付きフィールド、その後、意味論の運用レベルの純粋な関数型プログラミングにWRTを。
Applicative
したがって、適切に設計された宣言型言語は、表現できるものの一般性がいくらか失われますが、本質的に一貫性をもって表現できるものの利点はありますが、より明確に意味を表現できます。
前述の定義の例は、スプレッドシートプログラムのセル内の数式のセットです。これは、異なる列および行のセルに移動した場合、つまりセル識別子が変更された場合に同じ意味を与えるとは想定されていません。セル識別子は意図された意味の一部であり、不要ではありません。そのため、各スプレッドシートの結果は、一連の数式のセル識別子に対して一意です。この場合の一貫したモジュラーセマンティクスは、セル数式の純粋関数の入力および出力としてセル識別子を使用することです(以下を参照)。
ハイパーテキストマークアップ言語(HTML(静的Webページ用の言語)とも呼ばれます)は、(少なくともHTML 5以前は)動的な動作を表現する機能がなかった非常に(ただし完全ではない3)宣言型言語の例です。HTMLはおそらく学習が最も簡単な言語です。動的な動作の場合、JavaScriptなどの命令型スクリプト言語は通常HTMLと組み合わされていました。JavaScriptを使用しないHTMLは宣言型の定義に適合します。これは、各名義型(つまり、タグ)が、構文の規則内の構成の下で一貫した意味を維持するためです。
宣言の競合する定義は、セマンティックステートメントの可換性とべき等のプロパティです。つまり、意味を変更せずにステートメントを並べ替えたり複製したりできます。たとえば、名前付きフィールドに値を割り当てるステートメントは、それらの名前が暗黙の順序でモジュラーである場合、プログラムの意味を変更することなく、並べ替えたり複製したりできます。名前は、順序を意味する場合があります。たとえば、セル識別子には列と行の位置が含まれます。スプレッドシートで合計を移動すると、その意味が変わります。それ以外の場合、これらのプロパティは暗黙的にグローバルを必要としますセマンティクスの一貫性。順序と複製はセマンティクスに固有であるため、ステートメントのセマンティクスを設計してランダムに並べたり複製したりしても一貫性を保つことは一般に不可能です。たとえば、「Fooが存在する」(または構文)および「Fooが存在しない」(および破壊)というステートメントです。意図されたセマンティクスに固有のランダムな不整合を考慮する場合、宣言的なプロパティに対して十分に一般的であるとして、この定義を受け入れます。本質的に、この定義は一貫性をセマンティクスに直交させようとする、つまりセマンティクスの宇宙は動的に制限されておらず、グローバルコヒーレンスパラダイムでキャプチャできないという事実に対抗するため、一般化された定義として空虚です。
下位の操作セマンティクス(の構造評価順序)の可換性とべき等のプロパティが必要な場合、操作セマンティクスは宣言的なローカライズされたモジュラーセマンティクス、たとえば純粋な関数型プログラミング(命令ループの代わりに再帰を含む)に変換されます。次に、実装の詳細の運用順序は、上位レベルのセマンティクスの一貫性に影響を与えません(つまり、グローバルに広がります)。たとえば、すべての出力が計算されるまで、つまり純粋な関数に類似するまで、出力は入力にコピーされないため、スプレッドシートの式の評価順序(および理論的には複製)は重要ではありません。
C、Java、C ++、C#、PHP、JavaScriptは特に宣言型ではありません。Coputeの構文とPythonの構文は、意図的な結果、つまり無関係なものを排除する一貫した構文セマンティクスにより宣言的に結び付けられているため、コードを忘れた後でもすぐに理解できます。CoputeとHaskellは、運用上のセマンティクスの決定論を強制し、「自分自身を繰り返さないでください」(DRY)を奨励しています。
2プログラムのセマンティクスを証明できる場合でも、たとえば言語Coqを使用すると、これはタイピングで表現されるセマンティクスに限定され、タイピングはプログラムのすべてのセマンティクスをキャプチャできません。完全なチューリングではありません。たとえば、HTML + CSSを使用すると、一貫性のない組み合わせを表すことができ、そのためセマンティクスが未定義になります。
3多くの説明は、命令型プログラミングのみが構文的に順序付けられたステートメントを持っていると誤って主張しています。命令型プログラミングと関数型プログラミングの混乱を明確にしました。たとえば、HTMLステートメントの順序は、その意味の一貫性を低下させません。
編集:ロバートハーパーのブログに次のコメントを投稿しました。
関数型プログラミングでは...変数の変動範囲はタイプです
関数型プログラミングと命令型プログラミングをどのように区別するかに応じて、命令型プログラムでの「割り当て可能」には、その可変性に限界を設ける型もある場合があります。
現在関数型プログラミングで高く評価されている唯一の混乱しない定義は、a)ファーストクラスのオブジェクトおよびタイプとしての関数、b)ループ上の再帰の優先、および/またはc)純粋な関数(つまり、目的のセマンティクスに影響を与えない関数)メモされたときのプログラムの(したがって、完全な純粋な関数型プログラミングは、操作セマンティクス(メモリ割り当てなど)の影響により、汎用の意味セマンティクスには存在しません)。
純粋な関数のべき等プロパティは、その変数に対する関数呼び出しをその値で置き換えることができることを意味します。これは通常、命令手続きの引数には当てはまりません。純粋な関数は、入力タイプと結果タイプの間の構成されていない状態遷移に対して宣言的であるようです。
しかし、純粋な関数の構成は、そのような一貫性を維持しません。なぜなら、HaskellのIOMonadなどの純粋な関数型プログラミング言語で副作用(グローバル状態)命令プロセスをモデル化することが可能であり、さらに、そのようなことを防ぐことは完全に不可能だからです。チューリングの完全な関数型プログラミング言語。
2012年に書いたように、最近のブログでコメントのコンセンサスに似ているようですが、その宣言型プログラミングは、意図されたセマンティクスが決して不透明ではないという概念を捉えようとする試みです。不透明なセマンティクスの例は、順序への依存、操作上のセマンティクスレイヤーでの高レベルのセマンティクスの消去への依存(例:キャストは変換ではなく、具体化されたジェネリックスは高レベルのセマンティクスを制限する)、チェックできない変数値への依存(証明済み)正しい)プログラミング言語による。
したがって、Turing以外の完全な言語のみが宣言型であると結論付けました。
したがって、宣言型言語の明確で明確な属性の1つは、その出力が列挙可能な生成規則のセットに従っていることが証明できることです。たとえば、スクリプト化されていない(つまり、チューリングが完了していない)特定のHTMLプログラム(インタープリターの分岐方法の違いを無視)の場合、その出力の変動性は列挙可能です。またはもっと簡単に言えば、HTMLプログラムはその可変性の純粋な関数です。同様に、スプレッドシートプログラムは、その入力変数の純粋な関数です。
したがって、宣言型言語は無限の再帰の正反対であるように思え ます。つまり、ゲーデルの2番目の不完全性定理ごとに、自己参照定理は証明できません。
Lesie Lamport は、型と論理(カリーハワード対応など)を一致させることにより、プログラミング言語の文脈で数学の証明に適用されたゲーデルの不完全性定理にユークリッドがどのように対処したかについてのおとぎ話を書きました。
宣言型プログラミングは、「マシンの運用モデルではなく、開発者のメンタルモデルに準拠する言語でプログラミングする行為」です。
宣言型プログラミングと命令型プログラミングの違いは、構造化データの解析の問題によってよくわかります。
命令型プログラムは、相互に再帰的な関数を使用して入力を消費し、データを生成します。宣言型プログラムは、データを解析できるようにデータの構造を定義する文法を表現します。
これら2つのアプローチの違いは、宣言型プログラムは、ホスト言語よりも問題のメンタルモデルにより密接にマッピングされた新しい言語を作成することです。
私の知る限りでは、プロローグは(おそらく)物事を抽象的な方法で宣言することを目的としているため、プロローグのようなプログラミングシステムを記述するために使用され始めました。
それは上記のユーザーによって与えられた定義を持っているので、ますますほとんど意味がなくなります。HTMLの宣言型プログラミングとは対照的に、Haskellの宣言型プログラミングの間には溝があることは明らかです。
宣言型プログラミングの他のいくつかの例:
宣言型プログラミングは、コードのメンタルモデル *の簡素化に役立ち、最終的にはよりスケーラブルになる可能性があるため、優れています。
たとえば、配列またはリストの各要素に対して何かを行う関数があるとします。従来のコードは次のようになります。
foreach (object item in MyList)
{
DoSomething(item);
}
大したことはありません。しかし、より宣言的な構文を使用し、代わりにアクションとしてDoSomething()を定義するとどうなるでしょうか。次に、このように言うことができます:
MyList.ForEach(DoSometing);
もちろん、これはもっと簡潔です。しかし、あちこちに2行のコードを保存するだけではなく、もっと多くの懸念があると思います。たとえば、パフォーマンス。従来の方法では、処理を順番に実行する必要がありました。.ForEach()メソッドに、処理を並行して自動的に処理できることを通知する方法があった場合はどうなりますか?突然、非常に安全な方法でコードをマルチスレッド化し、コードを1行だけ変更しました。そして、実際には、.Netの拡張機能があり、それを行うことができます。