Declarative Programming Languagesに関する記事を読んでいました。
このタイプ/パラダイムのプログラミング言語の性質が理解できず、それが命令型言語とは対照的である場合、Haskell などのこのタイプのプログラミング言語でのプログラミングについて読んでから、後でその記事を読む必要がありますか?
Declarative Programming Languagesに関する記事を読んでいました。
このタイプ/パラダイムのプログラミング言語の性質が理解できず、それが命令型言語とは対照的である場合、Haskell などのこのタイプのプログラミング言語でのプログラミングについて読んでから、後でその記事を読む必要がありますか?
宣言型パラダイムのポイントは、怠惰であることです。私たち宣言型プログラマーは、コンパイラーにすべての作業を任せたいと思っています。可能な限り、使用したいアルゴリズムを指定せず、必要な結果の定義のみを指定します。
たとえば、命令型のセットアップで までの整数の合計を計算したい場合、n
(C で) 次のように記述できます。
int f(int n) {
int result = 0, i = 1;
for(;i <= n; i ++)
result += i;
return result;
}
宣言型のセットアップでは、この合計が何であるかを (Haskell で) 宣言するだけです。
f 0 = 0
f n = n + f (n - 1)
これはアルゴリズムではなく、単なる定義です。しかし、Haskell コンパイラはとにかく結果を得るのに十分賢いです。
次の標準的な例を使用して、Prolog に切り替えるとさらに見やすくなります。
さまざまな人々の間のいくつかの関係を宣言します。
father(luke, anakin).
father(anakin, a_slave_on_tattouin).
father(a_slave_on_tattouin, someone).
father(someone, adam).
次に、誰かの先祖が彼の父または彼の父の先祖であると述べます。
ancestor(Young, Old) :-
father(Young, Old).
ancestor(Young, VeryOld) :-
father(Young, Old),
ancestor(Old, VeryOld).
Prolog は魔法を解き放ち、次のようなクエリに答えることができます。
?- ancestor(luke, X).
X = anakin ;
X = a_slave_on_tattouin ;
X = someone ;
X = adam ;
false.
上記のことを読むためのいくつかの助け:ifHead :- Body
を意味します。上記のように、Head
Body
ancestor(Young, VeryOld) :-
father(Young, Old),
ancestor(Old, VeryOld).
isの父であり、isの祖先である場合、 is の祖先VeryOld
を意味します。Young
Old
Young
VeryOld
Old
and;
はまたはを意味します。したがって、ここで Prolog は、ルークにはanakin
、a_slave_on_tattouin
、 などの 4 つの祖先があることを示しています。
ご覧のとおり、アルゴリズムについては何も指定していませんが、それでも Prolog はluke
の系譜のすべての詳細を提供するなど、すばらしいことを実行できます。それは宣言型プログラミングの力です。あなたがデータを指定し、それがあなたの関心事であり、残りはコンパイラを扱う賢い人たちの仕事です。
Haskell や Prolog などを理解するには時間がかかるかもしれませんが、やる価値はあります。これは、宣言型と命令型を使い始めるためのアナロジーです。
x が 5.279 のとき、5sin(7x+20)+cos(x) が何であるかを知りたいと想像してみましょう。計算機しかありません。古い電卓の場合は、その方法と順序を指定する必要があります。MC
メモリを 0 にクリアし、メモリMR
を呼び出し、現在の数値をメモリ内の数値に追加するために使用する場合M+
、実際に入力するのは次のとおりです。
MC -- clear the memory
5.279 -- take the number 5.279
*7= -- multiply it by seven
+20= -- add twenty
sin= -- find sin of that
*5= -- times by five
M+ -- store that in the memory
5.279 -- take the number 5.279
cos -- find cos of it
M+ -- add that to what you had in the memory
MR -- get the total back out of memory onto the screen
電卓は数学を理解するようにプログラムされておらず、算術のみを理解するようにプログラムされているため、電卓にどのような計算を行うかを段階的に指示しています。あなたが計算しているものの構造は、それをステップに変えることによって不明瞭になっています. 一部の人々は、この段階的な方法を好み、非常に簡単で、他の人がなぜ他の方法でそれを行うのが面倒なのか理解できません. できます。電卓を信用する必要はありません。
現在、どのプログラミング言語や最新の計算機でも入力できます
5*sin(7*5.279+20)+cos(5.279)
これははるかに明確であり、過去 10 年間に購入した電卓の中には、それを可能にするものもあります。
5.279 -> x
5sin(7x+20)+cos(x)
最新の電卓では、問題を電卓の手順に分解するのではなく、自分の理解の観点から問題を表現できます。一部の人々は、この言いたいことを言うだけの方法を好み、それが非常に簡単であると感じ、他の人が他の方法でそれを行うのがなぜ面倒なのか理解できません. それは明確だ。計算機に説明する必要はありません。
これは、命令型プログラミング言語5.279 -> x; 5sin(7x+20)+cos(x)
でこの特定の問題を解決する方法とほぼ同じですが、電卓のスタイルについて話しているところです。古い電卓でそれを行う最初の方法は、はるかに必須です (ステップバイステップ)スタイルで、これは新しい電卓で、はるかに宣言的な(言いたいことを言う)スタイルです。
文脈を変えて、電卓を使って数字を計算する代わりに、フェルトペンを使って簡単でナンセンスな絵を描いてみましょう。測定するときは常に左上隅から始めると仮定しましょう。命令型(段階的な)スタイルは次のようになります
take a piece of paper 7 by 10
pick up a red pen
put it at the point (3,4)
draw a 2x4 rectangle here
colour it in
pick up a black pen
put it at the point (4,4)
draw a circle here radius 3
colour it in
一方、宣言型の just-say-what-you-means スタイルは次のようになります。
on a piece of paper 7 by 10
red 2x4 rectangle at (3,4)
underneath
black circle radius 3 at (4,4)
これらは命令型または宣言型のコードの例ではなく、考え方とプログラミングのスタイルの違いを示す試みです。宣言型プログラミングでは、言いたいことを言ってから、必然的に非常に賢いコンパイラを使用して、宣言を最適な命令シーケンスに変換します。あなたが言いたいことを言ったので、あなたの意味を維持しながら、これを達成するコードを最も効率的な形に根本的に変えることができます. あなたはコンパイラを信頼しています。
命令型プログラミングでは、やりたいことを段階的に言い、考えられる最も効率的な方法を使用したことを確認します。その後、コンパイラはいくつかのトリックを使用して不要な速度を排除し、より低い速度に変換します。 -レベルの命令シーケンス。物事を行う順序を指定したため、実行できる最適化の量には制限がありますが、プログラマーが既に最善の選択を行っていると信頼しています。
(私が信頼について話したのは、この草案を読んだ誰かが信頼が重要だと言ったからです。彼女は、電卓を宣言的な方法で行うと、実際には信頼できないため、命令的な方法で電卓を使用すると説明しています。多くのプログラマーは、宣言型であるほどコンパイラーを信頼していません - 彼らは制御を望んでおり、自分のコードを自分で最適化することを好みます.)
このことから、常に宣言型スタイルを使用する方がはるかに簡単であるように思えますが、通常、宣言型言語の学習曲線は急勾配であり、ほとんどの人は命令型を好みます。従来、速度には大きな違いがありました (命令型は、コンピューターの動作に直接的に似ているため、オーバーヘッドが少なくなります) が、宣言型コードの最新のコンパイラーの中には、多くの場合、速度テーブルの上位数個にあるようです - コンパイルされた詩解釈されたものは、命令型と宣言型よりもはるかに大きな違いを生むため、命令型であるにもかかわらず python などはしばしば遅くなります。
あなたの質問に答えるには、私は答えがノーであることをお勧めします。プログラミングを開始する必要があります。私は、プログラミング言語でプログラムを書かずに、プログラミング言語の有用性と適用性、またはそれらのさまざまな分類について十分に理解できるとは本当に信じていません。それらについて読むことは、プログラミングの代わりではなく、プログラミングを補完するものにしてください。
通常、宣言型プログラミングの例として指摘される言語はPrologです(これは、実際にはまだ私の「学ぶべき」リストにあります。SWI-Prologは、Squeeze の時点でDebian リポジトリで入手できます)。
もちろん、試してみるのが一番ですが、パラダイムの味を知りたいだけなら、いくつかのコード例をよく見てみるだけで十分かもしれません。
宣言型プログラミングとは、主にドメイン固有言語 (DSL) を発明し、これらのサブ言語でプログラミングすることです。 Haskellは、その特定のスタイルに非常に適しています。Haskell では、ほぼすべてのドメイン固有言語がホスト言語に組み込まれています。これらは組み込み DSL (EDSL) と呼ばれます。つまり、パーサーを作成する必要はありませんが、Haskell の洗練された軽量な構文を活用できます。いくつか例を挙げましょう。
画像/ビデオ処理をエンコードしたいとしましょう。ほぼすべてのフィルターに共通点があることがわかります。周囲に基づいてポイントを更新します。その特定の目的のために、comonadsと呼ばれる抽象的な概念があります。状態の変化を表現するのではなく、フィルターに関連付けられた遷移関数をエンコードします。つまり、データの相関関係と依存関係をエンコードします。ぼかしフィルターの信じられないほどの簡潔さと、それが写真とビデオの両方で機能するという事実に注目してください (すべてのFilterable
):
blur :: (Filterable img) => Radius -> img -> img
blur radius = extend (average . surroundingPoints radius)
グラフィカル ユーザー インターフェイス (GUI) をエンコードするとします。ほとんどすべての GUI に共通点があることがわかります。つまり、GUI の個々の要素は、基本的に時間によって変化するフィールドであり、それらの間に相関関係があります。その特定の目的のために、関数型リアクティブ プログラミング(FRP)と呼ばれる抽象的な概念があります。イベントを処理するのではなく、要素間のデータ関係を記述するだけです。
text1 = textField "text1"
text2 = textField "text2"
sum = fmap show (text1 + text2) <|> "Please enter numbers"
dialog =
label "First number:" ~|~ text1
~---~
label "Second number:" ~|~ text2
~---~
label "Sum:" ~|~ label sum
この説明には、ユーザーが数字を入力しなかった場合に何が起こるかなど、ダイアログ ボックスを作成するために必要なすべてが含まれています。これは特別な言語でも疑似コードでもありません。これは実際の Haskell コードです ( Netwireライブラリを使用)!