関数型(宣言型)と命令型プログラミングパラダイムの違いを指摘するために使用される標準の推論の「解決すること」と「解決する方法」を明確に理解したかどうかはわかりませんそれぞれ。
この違いが説明されるほとんどの場所では、いくつかのループ制御構造を使用して問題を解決する命令的な方法を示唆する例が補足され、再帰的方法を使用して問題を解決する宣言的な方法を示唆する。ループ制御構造と再帰的方法にあまりこだわっていません。
たとえば、私にとっては、リストの長さ(Haskellなど)を見つける方法は、「どのように問題を解決するか」とfor loop
c#/Java
およびリストの最後までカウンターを増やします。
Len :: [Int] -> Int
Len [] = 0
Len [x:xs] = 1 + Len(xs)
上記のコードスニペットは、「解決方法」の問題の別の言い方ですか?「はい」の場合、命令型プログラミングと宣言型プログラミングの「どのように」部分の境界を定義するほうがいいと思います。
いくつかのより良い例でこれを明確にするのを助けてください。ありがとう!!
上記のコードスニペットは、「解決方法」の問題の別の言い方ではありませんか?
はい。結局のところ、コンピュータサイエンスは常にコンピューティングに関連しているため、常に問題の「解決方法」について話しています。
しかし、機能的な例を特定の方法で読んだ場合:
Len :: [Int] -> Int
Len [] = 0
Len [x:xs] = 1 + Len(xs)
"Length is a function that takes a list of integers and returns an integer."
"Length of an empty list is zero."
"Length of a non-empty list is one plus the length of the 1..n-th elements of the list."
これらはすべてisステートメントです。彼らは何かが何であるかを宣言します。次に、(考案された)命令型バージョンを見てください。
length(x: [int]): int {
y = 0;
foreach(_ in x) {
y++;
}
return y;
}
"Length is a function that takes a list of integers and returns an integer."
"To calculate that, initialize a variable y with the value 0 -
then count the entries in your list, adding to y each time -
then return the value in variable y."
命令型プログラミングは何かを宣言するのではなく、何かを行う方法についてのレシピです。
もちろん、結局のところ、コンピューターは非常に似たようなことをすることになり、そのため、この種の区別はコードについて話すときにあまり役に立ちません。関数型プログラミング、命令型プログラミング、オブジェクト指向プログラミングは、プログラマーが問題についてどのように考えるかについてです。それらにどのようにアプローチしますか?それらをどのように説明しますか?特定の言語では、さまざまなモデルの表現が多少効率的になります。そのため、「関数型言語」や、「この言語を使用すると、機能について考えるプログラマーがコンピューターや他のプログラマーに対してコードでうまく表現できるようになります」などのショートカットについて話します。 。
私の意見では、構文的表現(命令的であるか機能的であるかを問わない)は、「何を」そして「どのように」とほぼ純粋に一致しています。数学の方程式との類似点をいくつか見ることができますが(解くべき方程式は「何」であり、方程式の解は「どのように」であるか)、一般的な意味で人々はそれを理解し、「何が」が問題に近く、「どのように」が近い解決策、「何を」および「どのように」は、問題ドメインの知識とその知識を処理する可能性との関係にあります。
違いは地理マップに似ています。マップ自体は道路のネットワークを表しており、「何を」です。パスファインダーは、2点を指定すると、AからBへの「移動」方法を提供します。その知識はすでにマップにあるため、繰り返しのタスクでは、特定のルートの説明よりもマップを用意する方が価値があります。地図を「読む」方法を知っている場合。
Haskell関数が「何を」または「どのように」であるかを尋ねるのはおそらく間違いです。これは道路のほんの一部であり、パスとマップの両方になる可能性があるためです。 「何を」「どのように」について話すことは、いくつかの情報システムを持っていることだけが理にかなっています。次に、宣言的な知識(オントロジー)といくつかの一般的なアルゴリズム(命令的またはその他)を使用すると、「何が」がより一般的であり、ドメイン内のはるかに多くの問題を解決できる可能性があるため、システムの価値が高まることがわかります。
代替案は、ほとんどすべてを「方法」として表現することです。これはおそらく十分な柔軟性がなく、通常はそれほど価値がありません。
言い換えると、知識を「何であるか」として表現することは、それをより有用にすることができますが、システムが「どのように」の部分を持っていることも前提としています:一部のインタープリター、ルールエンジン、論理推論者など。 」勝利はより大きなボリュームでもたらされます。宣言は洗練されたプログラミング言語ではめったに記述されないため、より多くの「何」を追加することは簡単であり、理想的にはインタープリター部分は同じままです。
それによると、Haskellスニペットは、それを操作するシステムがない限り、「方法」のように見えます(注:実行するだけではありません)。コードとデータの二重性です。
関数型プログラミングでは、「方法」は「この一連のステップを実行する」ではなく「この式を評価する」です。プログラマーとして、式をステップのシーケンスにほとんど自動的に変換して、それらを頭の中で評価することができるため、区別が常に明確であるとは限りません。また、FP式は、一連のステートメントによく似ています。
レイジー評価の複数のレイヤーを追加すると、多くの場合、明確な区別ができることがわかります。構成ファイルの可能な場所のリストがあり、それぞれにクラスターのリストがあり、それぞれにノードのリストがあるとします。必要以上の設定ファイルを読みたくありません。できる限り最初のノードに接続し、接続が成功した後、それ以上のノードへの接続を停止したい。あなたはそれをScalaのように書くかもしれません:
val validConfigs = allConfigFiles.filter(_.isValid)
val clusters = validConfigs.flatMap(_.getClusters)
val nodes = clusters.flatMap(_.getNodes)
val sessions = nodes.flatMap(_.getSession)
val validSession = sessions.headOption
allConfigFiles
、getClusters
、およびgetNodes
が LazyList または Observable のような遅延コレクションを返す場合、実際の評価順序は非常に興味深いです:
sessions
の最初の要素が要求されます。nodes
の最初の要素が要求されます。clusters
の最初の要素が要求されます。allConfigFiles
の最初の要素が要求されます。isValid
は最初の設定ファイルで呼び出されます。allConfigFiles
の次の要素が要求されます。isValid
は次の設定ファイルで呼び出されます。getClusters
が構成ファイルで呼び出されます。getNodes
が呼び出され、最初のノードが要求されます。getSession
が呼び出されます。clusters
から次のノードが要求されます。validConfigs
から要求されます。allConfigFiles
から要求されます。同じオンデマンド特性を取得しようとしながら、この同じコードを命令スタイルで作成すると、通常、特にいくつかのステップが例外のスローによる失敗を示している場合や、ファイルを閉じるなどのクリーンアップを行う必要がある場合に、ifステートメントとループが途切れます。ハンドル。
人々が「解決すべきこと」を指定していると言うとき、意図した結果が見やすいことが多いにもかかわらず、実際の評価順序はすぐにはわからないかもしれないという考えに言及しています。