関数型、宣言型、命令型プログラミングという用語はどういう意味ですか?
関数型、宣言型、命令型プログラミングという用語はどういう意味ですか?
回答:
これを書いている時点では、このページで投票された上位の回答は不正確であり、ウィキペディアを引用する回答を含め、宣言的定義と命令的定義が混同されています。いくつかの回答は、さまざまな方法で用語を融合させています。
数式がセルを変更するかどうかに関係なく、スプレッドシートプログラミングが宣言型である理由についての私の説明も参照してください。
また、関数型プログラミングは宣言型のサブセットでなければならないという回答もいくつかあります。その点では、「機能」と「手順」を区別するかどうかにかかっています。最初に命令型と宣言型を比較してみましょう。
宣言式の定義
唯一の可能性を区別することができる属性の宣言型からの発現が不可欠式は、そのサブ式の参照透過性(RT)です。他のすべての属性は、両方のタイプの式の間で共有されるか、RTから派生します。
100%宣言型言語(つまり、可能なすべての式がRTである言語)は、(他のRT要件の中で)保存された値の変更を許可しません(例:HTMLおよびほとんどのHaskell)。
RT式の定義
RTは「副作用なし」と呼ばれることがよくあります。効果という用語には正確な定義がないため、「副作用なし」がRTと同じであることに同意しない人もいます。RTには正確な定義があります。
すべてのサブ式は概念的には関数呼び出しであるため、RTでは、関数の実装(つまり、呼び出された関数内の式)が関数の外部にある可変状態にアクセスできないようにする必要があります(可変ローカル状態へのアクセスは許可されています)。簡単に言えば、関数(実装)は純粋でなければなりません。
純関数の定義
純粋な関数は、「副作用がない」とよく言われます。効果という用語には正確な定義がないため、同意しない人もいます。
純粋関数には次の属性があります。
RTは式(関数呼び出しを含む)に適用され、純粋性は関数(の実装)に適用されることに注意してください。
RT式を作成する不純な関数のあいまいな例は並行性ですが、これは純粋さが割り込み抽象化層で壊れているためです。あなたは本当にこれを知る必要はありません。RT式を作成するには、純粋な関数を呼び出します。
RTの派生属性
宣言型プログラミングで引用されたその他の属性、たとえばWikipediaで使用されている1999年の引用は、RTに由来するか、命令型プログラミングと共有されています。したがって、私の正確な定義が正しいことを証明します。
外部値の不変性は、RTの要件のサブセットであることに注意してください。
宣言型言語には、ループ制御構造がありません。たとえばfor
、およびwhile
、不変性により、ループ条件が変更されることはありません。
宣言型言語は、入れ子関数の順序(別名論理依存関係)以外の制御フローを表現しません。これは、 不変性により、他の評価順序を選択しても結果が変化しないためです(以下を参照)。
宣言型言語は論理的な「ステップ」(つまり、ネストされたRT関数呼び出し順序)を表現しますが、各関数呼び出しがより高いレベルのセマンティック(つまり「何をするか」)であるかどうかは、宣言型プログラミングの要件ではありません。命令型との違いは、不変性(より一般的にはRT)のため、これらの「ステップ」は変更可能な状態に依存できず、表現されたロジックの関係順序(つまり、関数呼び出しのネストの順序、別名サブ式)にのみ依存しないことです)。
たとえば、HTML段落<p>
は、段落内のサブ式(つまり、タグ)が評価されるまで表示できません。変更可能な状態はありません。タグ階層の論理関係(サブ式のネスト、これは同様にネストされた関数呼び出しです)による順序依存のみです。
したがって、不変性(より一般的にはRT)の派生属性があり、宣言式は構成部分(つまり、部分式関数引数)の論理関係のみを表し、変更可能な状態関係は表しません。
評価順序
部分式の評価順序の選択は、関数呼び出しのいずれかがRTではない(つまり、関数が純粋でない)場合にのみ変化する結果をもたらすことができます。たとえば、関数の外部の変更可能な状態が関数内でアクセスされます。
例えば、いくつかのネストされた式を与え、例えばf( g(a, b), h(c, d) )
、関数の引数の熱心と遅延評価は機能している場合、同じ結果が得られますf
、g
とh
純粋です。
一方で、機能ならばf
、g
とh
純粋でない場合、評価順序の選択は、異なる結果を与えることができます。
ネストされた式は概念的にはネストされた関数です。これは、式演算子が単項接頭辞、単項後置表記、または2進中置表記法に見せかけた単なる関数呼び出しだからです。
接線方向に、すべての識別子、例えばもしa
、b
、c
、d
、ある不変の機能は常に純粋で、その後、プログラムの外部の状態にアクセスすることができない、どこにでも(つまり、I / O)、および一切アブストラクションレイヤ切れがありません。
ちなみに、Haskellには異なる構文がありf (g a b) (h c d)
ます。
評価注文詳細
関数は、入力から出力への状態の遷移(変更可能な格納値ではない)です。純粋関数への呼び出しのRT構成の場合、これらの状態遷移の実行順序は独立しています。副作用がなく、RT関数がキャッシュされた値で置き換えられるという原則があるため、各関数呼び出しの状態遷移は他の関数呼び出しから独立しています。人気のある誤解を修正するために、純粋なモナドの構成は常に宣言的であり、RTですが、HaskellのIO
モナドは間違いなく不純であり、したがってWorld
プログラムの外部の状態に対して強制的であるという事実にもかかわらず(ただし、以下の警告の意味では、副作用)分離されています)。
熱心な評価は、関数が呼び出される前に関数の引数が評価されることを意味し、遅延評価は、引数が関数内でアクセスされるまで(およびその場合)引数が評価されないことを意味します。
定義:関数パラメーターは関数定義サイトで宣言され、関数引数は関数呼び出しサイトで提供されます。パラメータと引数の違いを知る。
概念的には、すべての式は、例えば、定数は入力せずに関数である関数呼び出し(の組成)は、単項演算子は、一方の入力を有する関数である、バイナリ挿入演算子は、2つの入力を有する関数であり、コンストラクタは関数であり、さらには制御文(例えばif
、for
、while
)関数でモデル化できます。これらのことを順番 引数の関数は(ネストされた関数呼び出しの順序ではない混同ん)が評価されているが、構文で宣言されていない、などがf( g() )
熱心に評価することができg
、その後f
にg
の結果やそれが評価することができf
、唯一いい加減に評価しg
、その結果を内部に必要になったときf
。
注意、チューリング完全な言語(つまり、無限の再帰を可能にする)は完全に宣言的ではありません。たとえば、遅延評価はメモリと時間の不確定性をもたらします。ただし、評価順序の選択によるこれらの副作用は、メモリ消費、実行時間、レイテンシ、非終了、および外部ヒステリシスに限定されているため、外部同期です。
関数型プログラミング
宣言型プログラミングはループを持つことができないため、反復する唯一の方法は関数の再帰です。この意味で、関数型プログラミングは宣言型プログラミングに関連しています。
ただし、関数型プログラミングは宣言型プログラミングに限定されません。機能構成は、特に式の問題に関して、サブタイプの追加と機能分解のいずれかによって拡張を実現できるサブタイピングと対照的です。拡張機能は、両方の方法を組み合わせることができます。
関数型プログラミングは通常、関数をファーストクラスのオブジェクトにします。つまり、関数の型は、他の型のどこにでも文法に現れることができます。要するに、関数は関数を入力して操作できるため、関数の構成を強調することにより、つまり決定論的計算のサブ計算間の依存関係を分離することで、関心の分離を実現できます。
たとえば、コレクションの各要素に適用される可能性のある無限の可能性のある特殊なアクションのそれぞれに対して、個別の関数を記述する(および関数の宣言が必要な場合はループの代わりに再帰を使用する)代わりに、関数型プログラミングは再利用可能な反復を使用します機能、例えばmap
、fold
、filter
。これらの反復関数は、ファーストクラスの特殊なアクション関数を入力します。これらの反復関数はコレクションを反復処理し、各要素の入力専用アクション関数を呼び出します。これらのアクション関数は、コレクションを反復するためのループステートメントを含める必要がないため、より簡潔です。
ただし、関数が純粋でない場合、それは実際にはプロシージャであることに注意してください。不純な関数を使用する関数型プログラミングは、実際には手続き型プログラミングであると言えるでしょう。したがって、宣言式がRTであることに同意した場合、手続き型プログラミングは宣言型プログラミングではないと言えるため、関数型プログラミングは常にRTであり、宣言型プログラミングのサブセットである必要があると主張できます。
並列処理
ファーストクラスの機能を備えたこの機能構成は、独立した機能を分離することにより、並列処理の深さを表現できます。
ブレントの原理:仕事wと深さdの計算は、時間O(max(w / p、d))でpプロセッサPRAMに実装できます。
並行性と並列性の両方とも、宣言型プログラミング、つまり不変性とRT も必要です。
では、並列性==同時実行性というこの危険な仮定はどこから来たのでしょうか。それは副作用を伴う言語の自然な帰結です:言語がいたるところに副作用を持っている場合、一度に複数のことを実行しようとするときはいつでも、各操作からの影響のインターリーブによって引き起こされる非決定性があります。したがって、副作用のある言語では、並列処理を実現する唯一の方法は並行性です。したがって、2つが混同していることがよくあるのは当然のことです。
評価の順序は、機能構成の終了とパフォーマンスの副作用にも影響することに注意してください。
Eager(CBV)とLazy(CBN)は、カテゴリの決闘[ 10 ]です。これは、評価の順序が逆になっているためです。つまり、外部関数と内部関数のどちらが最初に評価されるかです。逆さまのツリーを想像してみてください。それから、eagerは、関数ツリーのブランチのヒントからブランチ階層のトップレベルの関数トランクまで評価します。一方、怠惰は幹から枝の先端まで評価します。Eagerは論理積(「and」、a / k / aカテゴリカル製品)を持たず、lazyは論理積(「or」、a / k / aカテゴリ「合計」)を持ちません[ 11 ]。
パフォーマンス
熱心な
非終了の場合と同様に、熱意は連言機能的構成では熱心すぎます。つまり、構成制御構造は、遅延では行われない不要な作業を行います。以下のため例、熱心に熱心に不必要にそれが倍で構成されている場合、ブール値のリスト全体をマップする最初の真の要素で終了しています。
この不要な作業は、両方とも純粋な関数を使用して、eagerとlazyのシーケンシャルな時間の複雑さで主張されている「最大」の余分なlog n係数の原因です。解決策は、遅延コンストラクター(つまり、オプションの遅延生成物を使用したい)でファンクター(リストなど)を使用することです。熱心さの不正確さは、内部関数に起因するためです。これは、積が構成型、つまり初期固定点に初期代数をもつ帰納型であるためです[ 11 ]
怠惰な
非終了の場合と同様に、lazyは選言的機能構成では怠惰です。つまり、共帰的ファイナリティは必要以上に遅くなる可能性があり、不要な作業と遅延の非決定性の両方が発生し、eager [ 10 ] [ 11 ] とは異なります。 。最終性の例は、状態、タイミング、非終了、および実行時例外です。これらは必須の副作用ですが、純粋な宣言型言語(Haskellなど)でも、空間の割り当てに暗黙の命令型IOモナド(注:すべてのモナドが命令型ではない!)に状態があり、タイミングは命令型に相対的な状態です現実の世界。オプションの熱心な副産物でもレイジーを使用すると、「怠惰」が内部の副産物にリークします。レイジーでは、遅延の不正確さが外部関数に起因するためです。(「非終了」セクションの例を参照してください。ここで==は外部の二項演算子関数です)。これは、副産物がファイナリティ、つまり最終オブジェクトに最終代数を伴う共帰型によって制限されるためです[ 11 ]。
Lazyは、レイテンシとスペースの関数の設計とデバッグで不確定性を引き起こします。宣言された関数の階層と実行時の評価順序の間の不一致のため、そのデバッグはおそらく大多数のプログラマーの能力を超えています。熱心に評価された怠惰な純粋な関数は、実行時に以前見られなかった非終了を導入する可能性があります。逆に、lazyで評価される熱心な純粋関数は、実行時に以前には見えなかったスペースとレイテンシの不確定性をもたらす可能性があります。
非終了
コンパイル時に、チューリング完全言語での停止問題と相互再帰により、関数の終了は通常保証されません。
熱心な
熱心ではあるが怠惰ではなく、Head
"and"の結合のTail
場合、どちらHead
かTail
が終了しない場合、それぞれ左側List( Head(), Tail() ).tail == Tail()
またはList( Head(), Tail() ).head == Head()
右側が終了しないため、どちらかが真ではありません。
一方、レイジーでは、両側が終了します。したがって、eagerは接続積ではあまりにも熱心であり、不要な場合は終了しない(ランタイム例外を含む)。
怠惰な
遅延ではあるが意欲的ではない場合、1
"or"の分離のため、終了しない2
場合、左側は終了し、右側はf
終了しないList( f ? 1 : 2, 3 ).tail == (f ? List( 1, 3 ) : List( 2, 3 )).tail
ため、真ではありません。
一方、eagerの場合、どちらの側も終了しないため、同等性テストに到達することはありません。したがって、lazyは選言的副産物ではあまりにも怠惰であり、そのような場合には、eagerの場合よりも多くの作業を行った後(ランタイム例外を含む)に失敗します。
[ 10 ]宣言の継続とカテゴリの双対性、Filinski、セクション2.5.4 SCLにおけるCBVとCBNの比較、および3.6.1 CBVとCBN。
[ 11 ]宣言的継続とカテゴリー的双対性、Filinski、セクション2.2.1製品と連産品、2.2.2最終オブジェクトと初期オブジェクト、2.5.2 CBVと遅延製品、および2.5.3 CBNと積極的な連産品。
これらについては、明確で客観的な定義は実際にはありません。これが私がそれらを定義する方法です:
必須 -焦点は、コンピュータが実行すること(例:C、C ++、Java)ではなく、コンピュータが実行する必要のあるステップにあります。
宣言 -焦点は、コンピュータが実行する方法(SQLなど)ではなく、コンピュータが実行する必要があることです。
関数型 -再帰に重点を置いた宣言型言語のサブセット
命令型と宣言型は、2つの相反するプログラミングスタイルを記述しています。命令型は伝統的な「ステップバイステップのレシピ」アプローチであり、宣言型はより「これが私が欲しいものであり、今あなたはそれを行う方法を考え出す」です。
これら2つのアプローチは、同じ言語と同じプログラムであっても、プログラミング全体で発生します。プログラマーは多くの詳細を指定する必要がなく、バグの可能性も少ないため、一般的に宣言型のアプローチが望ましいと考えられています(必要な結果を記述し、十分にテストされた自動プロセスがそれから逆方向に機能する場合)ステップを定義すると、手作業で各ステップを指定するよりも信頼性が高まることが期待できます。
一方、命令型アプローチでは、より低レベルの制御が可能になります。これは、プログラミングに対する「マイクロマネージャーアプローチ」です。これにより、プログラマーは問題に関する知識を活用して、より効率的な答えを得ることができます。そのため、プログラムの一部の部分がより宣言的なスタイルで記述されることは珍しくありませんが、速度が重要な部分はより必須です。
ご想像のとおり、プログラムの記述に使用する言語は、どのように宣言できるかに影響を与えます-結果の説明から何をすべきかを判断するための組み込みの「スマート」を備えた言語は、はるかに宣言的なものになりますプログラマが最初にそのようなインテリジェンスを命令型コードで追加してから、より宣言的な層を上に構築できるようにする必要がある場合よりもアプローチします。したがって、たとえば、プロローグのような言語は、回答を検索するプロセスが組み込まれているため、非常に宣言的であると見なされます。
これまでのところ、関数型プログラミングについては触れていません。それは、その意味が他の2つと直接関係しない用語であるためです。最も単純な関数型プログラミングでは、関数を使用します。特に、「ファーストクラスの値」として関数をサポートする言語を使用すること。つまり、関数を作成できるだけでなく、関数を作成する(関数を作成する)関数を作成して、関数を渡すことができます。機能。つまり、その関数は文字列や数値のようなものと同じくらい柔軟で一般的です。
奇妙に思えるかもしれませんが、関数型、命令型、宣言型が一緒に言及されることがよくあります。これは、関数型プログラミングの考え方を「極端に」とらえた結果です。関数は、最も純粋な意味で、数学からの何か-ある入力を受け取り、常に同じ出力を与える一種の「ブラックボックス」です。そして、そのような振る舞いは変化する変数を保存することを必要としません。したがって、関数型プログラミングの非常に純粋で数学的な影響を受けたアイデアを実装することを目的とするプログラミング言語を設計する場合、(特定の、限定された、技術的な意味で)変化する可能性のある値のアイデアを主に拒否することになります。
そして、それを行う場合-変数の変更方法を制限すると-ほとんどの場合、命令型プログラミングの大部分が変数の変更方法を記述しているため、プログラマーはより宣言的なプログラムを作成しなければならず、もはやそれを行う!したがって、関数型プログラミング、特に関数型言語でのプログラミングは、より宣言的なコードを提供する傾向があることがわかります。
要約すると、次のようになります。
命令型と宣言型は、2つの反対のプログラミングスタイルです(これらのスタイルを奨励するプログラミング言語には同じ名前が使用されます)。
関数型プログラミングはプログラミングのスタイルであり、関数は非常に重要になり、結果として値の変更はそれほど重要ではなくなります。値の変更を指定する機能が制限されているため、より宣言的なスタイルが強制されます。
したがって、「関数型プログラミング」はしばしば「宣言型」と表現されます。
手短に:
命令型言語は、(それを行う、その後、これを行う)シーケンス内のコンピュータが実行することを一連の命令をspecfies。
宣言型言語は、出力が、そこからの入力(例。あなたがAを持っている場合、その結果はBである)結果としてどうあるべきかについてのルールのセットを宣言します。エンジンはこれらのルールを入力に適用し、出力を提供します。
関数型言語は、入力を出力に変換される方法を定義する数学的/論理関数のセットを宣言する。例えば。f(y)= y * y。それは一種の宣言型言語です。
必須:目標を達成する方法
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
命令型プログラミングとは、コンピューターによって実行される操作がどのように行われるかを記述する命令からプログラムが構成されている、あらゆるスタイルのプログラミングを意味します。
宣言型プログラミングとは、プログラムが問題または解決策のいずれかの記述であるあらゆるスタイルのプログラミングを意味しますが、作業がどのように行われるかを明示的に述べていません。
関数型プログラミングは、関数と関数の関数を評価することによるプログラミングです...(厳密に定義された)関数型プログラミングは、副作用のない数学関数を定義することによるプログラミングを意味するため、宣言型プログラミングの一種ですが、これは唯一の種類の宣言型プログラミングではありません。
ロジックプログラミング(Prologなど)は、宣言型プログラミングの別の形式です。これには、論理ステートメントが真であるかどうか(またはそれが満たされるかどうか)を決定することによる計算が含まれます。プログラムは通常、一連の事実とルール、つまり一連の指示ではなく説明です。
用語書き換え(CASLなど)は、宣言型プログラミングの別の形式です。代数的用語の記号変換が含まれます。論理プログラミングや関数型プログラミングとは完全に異なります。
命令型 -式は実行する一連のアクションを記述します(連想)
宣言的 -式は、プログラムの動作に寄与する宣言です(連想、可換、べき等、単調)
関数型 -式には効果のみの価値があります。セマンティクスは方程式の推論をサポートします
以前の回答を書いてから、以下に引用する宣言的プロパティの新しい定義を作成しました。また、命令型プログラミングをデュアルプロパティとして定義しました。
この定義は簡潔であり、より一般的であるため、以前の回答で提供した定義よりも優れています。しかし、プログラミングや生活全般に当てはまる不完全性の定理の意味は人間が心を包み込むのが難しいため、理解するのはもっと難しいかもしれません。
引用されている定義の説明では、宣言型プログラミングで純粋関数型プログラミングが果たす役割について説明しています。
以下の定義はそれらが双対であると主張しているため、すべてのエキゾチックなプログラミングは、宣言と命令の次の分類に適合します。
宣言型と命令型
宣言的なプロパティは奇妙で鈍く、技術的に正確な定義で捉えることは困難であり、一般的で曖昧ではありません。これは、意図しない副作用を招くことなくプログラムの意味(別名セマンティクス)を宣言できるからです。意味の表現と意図しない影響の回避との間には固有の緊張があり、この緊張は実際にはプログラミングと宇宙の不完全性定理に由来しています。
これは、技術的に不正確な単純化、およびとして宣言型を定義することがしばしば曖昧である「何をすべきか」などと不可欠「どのように行います」。あいまいなケースは、プログラムを出力するプログラム(コンパイラ)の「何」が「どのように」であるかです。
明らかに、チューリング言語を完全なものにする無制限の再帰も、評価の構文構造だけでなく、セマンティクスにも類似しています(操作上のセマンティクス)。これは論理的にはゲーデルの定理に類似した例です。「公理の完全なシステムにも一貫性がない」。その引用の矛盾した奇妙さを考えてください!したがって、我々は証明できない、また、意味論の発現が証明可能に拘束されていない方法を示す例である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 は、ユークリッドがプログラミング言語の文脈で数学の証明に適用されたゲーデルの不完全性定理を、型と論理(カリーハワード対応など)の間で一致させることによってどのように処理したかについておとぎ話を書きました。
命令型プログラミング:何かを行う方法を「マシン」に伝えることで、結果として何が起こりたいかが起こります。
宣言型プログラミング:「マシン」に何を起こさせたいかを伝え、コンピュータにその方法を理解させる。
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);
}
注:違いは、簡潔さ、複雑さ、抽象化のいずれでもありません。述べたように、違いがあるどのように対するもの。
命令型/宣言/機能性の面では、一般的な言語を分類するため、過去に良好であったが、今日ですべての「大言語」で、いくつかのオプション(典型的には(Javaの、Pythonのは、JavaScriptなど)フレームワーク「他の焦点」と表現する)をその主なもの(通常は必須)よりも、並列プロセス、宣言関数、ラムダなどを表現するため
したがって、この質問の良い変形は「今日のフレームワークを分類するためにどの側面が良いのか?」 ...重要な側面は、「プログラミングスタイル」とラベル付けできるものです...
説明する良い例。ウィキペディアでjQueryについて読むことができるように、
jQueryコア機能のセット— DOM要素の選択、トラバーサル、操作—は、そのセレクターエンジン(...)によって有効化され、新しい「プログラミングスタイル」、融合アルゴリズム、およびDOMデータ構造を作成しました
したがって、jQueryは「新しいプログラミングスタイル」に焦点を当てた最良の(人気のある)例です。これは、オブジェクト指向だけでなく、「アルゴリズムとデータ構造の融合 」です。jQueryは、スプレッドシートとしては多少反応しますが、「セル指向」ではなく、「DOMノード指向」です... このコンテキストでの主なスタイルの比較:
融合なし:すべての「大きな言語」、関数型/宣言型/命令型表現では、通常、データとアルゴリズムの「融合なし」ですが、オブジェクト指向を除き、厳密な代数構造の観点での融合です。
いくつかの融合:融合のすべての古典的な戦略、今日では、それをパラダイムとして使用するいくつかのフレームワークがあります... データフロー、 イベント駆動型プログラミング(またはawkおよびXSLTのような古いドメイン固有の言語)...現代のスプレッドシートを使用したプログラミングと同様に、リアクティブプログラミングスタイルの例。
大きな融合:「jQueryスタイル」です... jQueryは、「アルゴリズムとDOMデータ構造の融合」に焦点を当てたドメイン固有の言語です。 PS:その他の「クエリ言語」、XQuery、SQL(命令式オプションとしてPLを使用)もデータアルゴリズムの融合の例ですが、他のシステムモジュールとの融合がないアイランドです...-バリアントを使用する場合の Springと仕様節は、別の良い融合の例です。find()
宣言型プログラミングとは、入力と出力の間に時間のないロジックを表現することによるプログラミングです。たとえば、擬似コードでは、次の例は宣言型になります。
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つが目的の結果を保持するまでメモリアドレスを手動で変更しました。すべての言語が宣言的意味論をある程度拡張できることは明らかですが、すべてが命令型を許可するわけではなく、一部の「純粋に」宣言型言語は副作用と変異を完全に許可します。
宣言型言語は、「実行方法」ではなく「実行する必要があること」を指定しているとよく言われますが、これは誤解です。宣言型プログラムでは、入力から出力への移動方法を指定していますが、別の方法では、指定する関係は効果的に計算可能である必要があります(重要な用語。わからない場合は調べてください)。別のアプローチは非決定的プログラミングです。これは、実装が成功するまで試行錯誤ですべてのパスを使い果たす前に、結果が満たす条件を実際に指定するだけです。
純粋な宣言型言語には、HaskellとPure Prologが含まれます。一方から他方へのスライディングスケールは、Pure Prolog、Haskell、OCaml、Scheme / Lisp、Python、Javascript、C-、Perl、PHP、C ++、Pascall、C、Fortran、Assemblyです。
factorial
が値を変更しないことを確認してください。
あなたの分類法は間違っていると思います。命令型と宣言型の2つの反対のタイプがあります。関数型は宣言型のサブタイプにすぎません。ところで、ウィキペディアは同じ事実を述べています。
簡単に言えば、プログラミングスタイルが強調するほど(実行する方法)の詳細を抽象化して(実行する)方法を強調するほど、そのスタイルは宣言的であると見なされます。命令に対しては、反対のことが当てはまります。関数型プログラミングは宣言型スタイルに関連付けられています。