C#、Visual Basic、C ++、Javaなどのオブジェクト指向プログラミング(OOP)言語を含むほとんどの主流言語は、主に命令型(手続き型)プログラミングをサポートするように設計されていますが、Haskell / goferのような言語は純粋に機能的です。これらの2つのプログラミング方法の違いは何ですか?
プログラミングの方法を選択することはユーザーの要件に依存することはわかっていますが、関数型プログラミング言語を学ぶことが推奨されるのはなぜですか?
C#、Visual Basic、C ++、Javaなどのオブジェクト指向プログラミング(OOP)言語を含むほとんどの主流言語は、主に命令型(手続き型)プログラミングをサポートするように設計されていますが、Haskell / goferのような言語は純粋に機能的です。これらの2つのプログラミング方法の違いは何ですか?
プログラミングの方法を選択することはユーザーの要件に依存することはわかっていますが、関数型プログラミング言語を学ぶことが推奨されるのはなぜですか?
回答:
定義: 命令型言語は一連のステートメントを使用して、特定の目標を達成する方法を決定します。これらのステートメントは、それぞれが順番に実行されるときにプログラムの状態を変更すると言われています。
例: Javaは命令型言語です。たとえば、一連の数値を追加するプログラムを作成できます。
int total = 0;
int number1 = 5;
int number2 = 10;
int number3 = 15;
total = number1 + number2 + number3;
各ステートメントは、各変数への値の割り当てからそれらの値の最後の加算まで、プログラムの状態を変更します。5つのステートメントのシーケンスを使用して、プログラムは数値5、10、および15を加算する方法を明示的に通知されます。
関数型言語: 関数型プログラミングのパラダイムは、問題解決のための純粋な関数型アプローチをサポートするために明示的に作成されました。関数型プログラミングは、宣言型プログラミングの一種です。
純粋関数の利点: 関数変換を純粋関数として実装する主な理由は、純粋関数が構成可能であること、つまり、自己完結型でステートレスであることです。これらの特性には、次のような多くの利点があります。可読性と保守性の向上。これは、各関数がその引数を指定して特定のタスクを実行するように設計されているためです。関数は外部状態に依存しません。
より簡単な繰り返し開発。コードはリファクタリングが容易であるため、設計の変更は多くの場合、実装が容易です。たとえば、複雑な変換を記述し、変換でいくつかのコードが数回繰り返されていることに気付いたとします。純粋なメソッドを介してリファクタリングする場合、副作用を心配することなく、自由に純粋なメソッドを呼び出すことができます。
簡単なテストとデバッグ。純粋な関数は分離してより簡単にテストできるため、一般的な値、有効なエッジケース、無効なエッジケースで純粋な関数を呼び出すテストコードを記述できます。
OOP Peopleまたは命令型言語の場合:
オブジェクト指向言語は、物事に一定の操作セットがあり、コードが進化するにつれて主に新しい物を追加する場合に適しています。これは、既存のメソッドを実装する新しいクラスを追加することで実現でき、既存のクラスはそのままになります。
関数型言語は、固定されたものがあり、コードが進化するにつれて、主に既存のものに新しい操作を追加する場合に適しています。これは、既存のデータ型で計算する新しい関数を追加することで達成でき、既存の関数はそのままになります。
短所:
プログラミングの方法を選択することはユーザーの要件に依存するため、ユーザーが適切な方法を選択しない場合にのみ害が生じます。
進化がうまくいかない場合、問題があります:
ここに違いがあります:
必須:
...などなど...
宣言型、その機能はサブカテゴリです。
...などなど...
概要:命令型言語では、メモリ内のビット、バイト、およびワードを変更する方法とその順序をコンピュータに指示します。機能的なものでは、物事やアクションなどをコンピュータに伝えます。たとえば、0の階乗は1であり、他のすべての自然数の階乗はその数とその前任者の階乗の積です。言わない:nの階乗を計算するには、メモリ領域を予約して1を格納し、そのメモリ領域の数値に2からnまでの数値を掛けて、同じ場所と最後に結果を格納します。メモリ領域には階乗が含まれます。
最近のほとんどの言語は、命令型と関数型の両方で程度は異なりますが、関数型プログラミングをよりよく理解するには、java / c#などの関数型言語ではない命令型コードとは対照的に、Haskellのような純粋な関数型言語の例を取り上げるのが最善です。例で説明するのはいつでも簡単だと思いますので、以下はその1つです。
関数型プログラミング:nの階乗、つまりnを計算します!つまり、nx(n-1)x(n-2)x ... x 2 X 1
-- | Haskell comment goes like
-- | below 2 lines is code to calculate factorial and 3rd is it's execution
factorial 0 = 1
factorial n = n * factorial (n - 1)
factorial 3
-- | for brevity let's call factorial as f; And x => y shows order execution left to right
-- | above executes as := f(3) as 3 x f(2) => f(2) as 2 x f(1) => f(1) as 1 x f(0) => f(0) as 1
-- | 3 x (2 x (1 x (1)) = 6
Haskelでは、引数値のレベルまで関数をオーバーロードできることに注意してください。以下は、命令性の度合いが増す命令コードの例です。
//somewhat functional way
function factorial(n) {
if(n < 1) {
return 1;
}
return n * factorial(n-1);
}
factorial(3);
//somewhat more imperative way
function imperativeFactor(n) {
int f = 1
for(int i = 1; i <= n; i++) {
f = f * i
}
return f;
}
この読み取りは、命令型コードがどのように部分、マシンの状態(i in forループ)、実行順序、フロー制御に重点を置いているかを理解するための良い参考になります。
後者の例は、大まかにjava / c#langコードと見なすことができ、最初の部分は、Haskellが値(ゼロ)で関数をオーバーロードするのとは対照的に、言語自体の制限と見なすことができるため、純粋な関数型言語ではないと言えます。あなたはそれが機能的progをサポートすると言うことができます。ある程度。
開示:上記のコードはどれもテスト/実行されていませんが、うまくいけば、コンセプトを伝えるのに十分なはずです。また、そのような修正についてコメントをいただければ幸いです:)
return n * factorial(n-1);
?
n * (n-1)
関数型プログラミングは宣言型プログラミングの一種であり、計算のロジックを記述し、実行順序は完全に重視されていません。
問題:この生物を馬からキリンに変えたい。
各項目を任意の順序で実行して、同じ結果を生成できます。
命令型プログラミングは手続き型です。状態と順序は重要です。
問題:車を駐車したい。
各ステップは、目的の結果に到達するために実行する必要があります。ガレージのドアが閉まっている状態でガレージに引っ張ると、ガレージのドアが壊れます。
関数型プログラミングは「関数を使用したプログラミング」であり、関数には、参照の透過性など、いくつかの予期される数学的特性があります。これらのプロパティから、追加のプロパティが流れます。特に、数学的証明につながる代替可能性によって有効になる一般的な推論ステップ(つまり、結果の信頼性の正当化)が流れます。
したがって、関数型プログラムは単なる表現にすぎません。
式が参照として透過的ではなくなった(したがって、関数と値で構築されず、それ自体が関数の一部になることができない)命令型プログラムの場所に注目することで、2つのスタイルのコントラストを簡単に確認できます。最も明白な2つの場所は次のとおりです。変異(例:変数)その他の副作用非ローカル制御フロー(例:例外)
関数と値で構成されるプログラムとしてのこのフレームワークでは、言語、概念、「関数型パターン」、コンビネーター、およびさまざまな型システムと評価アルゴリズムの実用的なパラダイム全体が構築されます。
最も極端な定義では、ほとんどすべての言語(CやJavaを含む)を関数型と呼ぶことができますが、通常、用語は、特に関連する抽象化(クロージャー、不変値、パターンマッチングなどの構文支援など)を備えた言語用に予約されています。関数型プログラミングの使用に関する限り、関数の使用を含み、副作用なしでコードをビルドします。証明を書くために使用
命令型プログラミングスタイルは、2005年から2013年までWeb開発で実践されていました。
命令型プログラミングを使用して、アプリケーションが実行する必要があることを正確にリストしたコードを段階的に作成しました。
関数型プログラミングスタイルは、関数を組み合わせる巧妙な方法によって抽象化を生み出します。
答えの中で宣言型プログラミングについての言及があり、それに関して宣言型プログラミングは従うべきいくつかのルールをリストアップしていると言います。次に、初期状態として参照するものをアプリケーションに提供し、それらのルールにアプリケーションの動作を定義させます。
さて、これらの簡単な説明はおそらくあまり意味をなさないので、類推を通して、命令型プログラミングと宣言型プログラミングの違いを見てみましょう。
ソフトウェアを構築しているのではなく、生計を立てているパイを想像してみてください。おそらく私たちは悪いパン屋であり、おいしいパイを私たちが本来あるべき方法で焼く方法を知りません。
だから私たちの上司は私たちに私たちがレシピとして知っている方向のリストをくれます。
レシピはパイの作り方を教えてくれます。1つのレシピは、次のような命令スタイルで記述されます。
宣言的レシピは次のことを行います。
小麦粉1カップ、卵1個、砂糖1カップ-初期状態
ルール
したがって、必須のアプローチは、段階的なアプローチによって特徴付けられます。ステップ1から始めて、ステップ2などに進みます。
最終的に、最終製品がいくつか得られます。このパイを作るには、これらの材料を混ぜ合わせ、鍋とオーブンに入れれば、最終製品ができあがります。
宣言型の世界では違います。宣言型のレシピでは、レシピを2つの部分に分け、最初は変数などのレシピの初期状態をリストする1つの部分から始めます。したがって、ここでの変数は、成分の量とそのタイプです。
私たちは初期状態または初期成分を取り、それらにいくつかのルールを適用します。
したがって、私たちは初期状態を取り、ルバーブストロベリーパイなどを食べる準備ができるまで、これらのルールを何度も繰り返します。
したがって、宣言型アプローチでは、これらのルールを適切に構成する方法を知る必要があります。
だから、私たちが私たちの成分や状態を調べたいと思うかもしれない規則は、混合されている場合、それらをフライパンに入れます。
当初の状態では、まだ材料を混ぜていないため、これは一致しません。
したがって、ルール2は、それらが混合されていない場合は、ボウルで混合します。はい、このルールが適用されます。
これで、状態として混合成分のボウルができました。
次に、その新しい状態をルールに再度適用します。
したがって、ルール1は、材料が混合されている場合、それらをフライパンに入れると言います。そうです、今はルール1が適用されるので、実行してみましょう。
これで、材料が混合されて鍋に入れられた新しい状態になりました。ルール1はもはや関係がなく、ルール2は適用されません。
ルール3は、材料がフライパンにある場合はオーブンに入れます。この新しいルールは、この新しい状態に適用されるものです。
そして、美味しいホットアップルパイなどになってしまいます。
さて、あなたが私のようであれば、あなたは考えているかもしれません、なぜ私たちはまだ命令型プログラミングをしていないのですか?意味あり。
まあ、単純なフローの場合はそうですが、ほとんどのWebアプリケーションには、命令型プログラミング設計では適切にキャプチャできないより複雑なフローがあります。
宣言的アプローチでは、次のようないくつかの初期成分または初期状態がある場合があります textInput=“”
、単一の変数のます。
たぶん、テキスト入力は空の文字列として始まります。
この初期状態を取得して、アプリケーションで定義された一連のルールに適用します。
ユーザーがテキストを入力した場合は、テキスト入力を更新します。さて、今は当てはまりません。
テンプレートがレンダリングされる場合は、ウィジェットを計算します。
まあ、これは当てはまらないので、プログラムはイベントが発生するのを待つだけです。
したがって、ある時点でユーザーがテキスト入力を更新し、ルール番号1を適用する場合があります。
更新する場合があります “abcd”
したがって、textとtextInputの更新を更新しました。ルール番号2は適用されません。ルール番号3は、テキスト入力が更新された場合に発生したもので、テンプレートを再レンダリングしてから、ルール2に戻ります。 、ウィジェットを計算します。さあ、ウィジェットを計算しましょう。
一般に、プログラマーとして、より宣言的なプログラミング設計を目指しています。
命令型の方がより明確で明白に見えますが、宣言型のアプローチは、より大きなアプリケーションに非常にうまく対応します。
関数型プログラミングを命令型で表現することは可能だと思います:
if... else
/ switch
ステートメントの 多くの状態チェックを使用するそのようなアプローチには大きな問題があります:
オブジェクトのような関数/メソッドを扱い、ステートレス性を採用する関数型プログラミングは、私が信じているこれらの問題を解決するために生まれました。
使用例:Android、iOSなどのフロントエンドアプリケーション、またはウェブアプリのロジックを含む。バックエンドとの通信。
命令型/手続き型コードで関数型プログラミングをシミュレートする際のその他の課題:
結局のところ、関数型コードは、コンパイラーによって命令型/手続き型であるアセンブリーコードまたはマシンコードに変換されると考えています。ただし、アセンブリを作成しない限り、人間は高レベル/人間が読める言語でコードを書くため、リストされているシナリオでは、関数型プログラミングがより適切な表現方法です。