web-dev-qa-db-ja.com

関数型プログラミング言語と命令型プログラミング言語の違いは何ですか?

C#、Visual Basic、C++、Javaなどのオブジェクト指向プログラミング(OOP)言語を含む主流言語のほとんどは、主に命令型(手続き型)プログラミングをサポートするように設計されていますが、Haskell/goferのような言語は純粋に機能します。これら2つのプログラミング方法の違いは何ですか?

プログラミングの方法を選択するのはユーザーの要件に依存しますが、なぜ関数型プログラミング言語を学ぶことが推奨されるのですか?

123
Swapnil Kotwal

定義:命令型言語は、一連のステートメントを使用して、特定の目標に到達する方法を決定します。これらのステートメントは、それぞれが順番に実行されるときにプログラムの状態を変更すると言われています。

例: Javaは命令型言語です。たとえば、一連の数字を追加するプログラムを作成できます。

 int total = 0;
 int number1 = 5;
 int number2 = 10;
 int number3 = 15;
 total = number1 + number2 + number3; 

各ステートメントは、各変数への値の割り当てから、それらの値の最終的な追加まで、プログラムの状態を変更します。 5つのステートメントのシーケンスを使用して、5、10、15の数字を加算する方法をプログラムに明示的に伝えます。

関数型言語:関数型プログラミングパラダイムは、問題解決に対する純粋な関数型アプローチをサポートするために明示的に作成されました。関数型プログラミングは、宣言型プログラミングの一種です。

純粋関数の利点:関数変換を純粋関数として実装する主な理由は、純粋関数が構成可能であることです。つまり、自己完結型でステートレスです。これらの特性には、次のような多くの利点があります。可読性と保守性の向上。これは、各関数が引数を指定して特定のタスクを実行するように設計されているためです。関数は外部状態に依存しません。

より簡単な反復開発。コードはリファクタリングが容易であるため、多くの場合、設計の変更は実装が容易です。たとえば、複雑な変換を記述し、その変換でいくつかのコードが数回繰り返されることに気付いたとします。純粋なメソッドを介してリファクタリングする場合、副作用を心配することなく、自由に純粋なメソッドを呼び出すことができます。

より簡単なテストとデバッグ。純粋な関数は単独でより簡単にテストできるため、典型的な値、有効なEdgeケース、および無効なEdgeケースで純粋な関数を呼び出すテストコードを作成できます。

OOP人または命令型言語の場合:

オブジェクト指向言語は、物に対して一定の操作セットがあり、コードが進化するにつれて主に新しいものを追加する場合に適しています。これは、既存のメソッドを実装する新しいクラスを追加することで実現でき、既存のクラスはそのまま残されます。

関数型言語は、固定されたものがあり、コードが進化するにつれて、主に既存のものに新しい操作を追加するときに役立ちます。これは、既存のデータ型で計算する新しい関数を追加することで実現でき、既存の関数はそのまま残されます。

短所:

プログラミングの方法を選択するのはユーザーの要件に依存するため、ユーザーが適切な方法を選択しない場合にのみ害があります。

進化が間違った方向に進むと、問題が生じます。

  • オブジェクト指向プログラムに新しい操作を追加するには、新しいメソッドを追加するために多くのクラス定義を編集する必要がある場合があります
  • 機能プログラムに新しい種類のものを追加するには、多くの機能定義を編集して新しいケースを追加する必要があります。
131
user2102654

違いは次のとおりです。

必須:

  • 開始
  • 靴のサイズ9 1/2をオンにします。
  • キーの配列[7]を保持するためにポケットにスペースを作ります。
  • 部屋に鍵を入れて、鍵をポケットに入れます。
  • ガレージに入ります。
  • オープンガレージ。
  • 車を入力してください。

...などなど

  • 牛乳を冷蔵庫に入れます。
  • やめる。

宣言的で、機能はサブカテゴリです:

  • 乳糖の消化に問題がない限り、牛乳は健康的な飲み物です。
  • 通常、牛乳は冷蔵庫に保存します。
  • 冷蔵庫は物を冷やすための箱です。
  • ストアは、アイテムが販売される場所です。
  • 「販売」とは、物をお金と交換することを意味します。
  • また、物とお金の交換は「購入」と呼ばれます。

...などなど...

  • 冷蔵庫に牛乳があることを確認してください(必要な場合-怠laな関数型言語の場合)。

要約:命令型言語では、メモリ内のビット、バイト、およびワードをどのように変更するか、どのような順序でコンピュータに指示するのか。機能的なものでは、コンピューターに何か、アクションなどが何であるかを伝えます。たとえば、0の階乗は1であり、他のすべての自然数の階乗は、その数とその前の階乗の階乗であると言います。言うことはありません:nの階乗を計算し、メモリ領域を予約して1を保存し、そのメモリ領域の数値に2からnの数値を掛けて、同じ場所に結果を格納し、最後に、メモリ領域には階乗が含まれます。

198
Ingo

ほとんどの現代言語は命令型と関数型の両方で程度はさまざまですが、関数型プログラミングをよりよく理解するには、Java/c#のような関数型ではない命令型コードとは対照的に、Haskellのような純粋な関数型言語の例を取り上げるのが最善です。例で説明するのは常に簡単だと思うので、以下にその1つを示します。

関数型プログラミング:nの階乗を計算する、つまりn!つまり、n x(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;
}

この read は、命令コードがどのように部分、マシンの状態(i for forループ)、実行の順序、フロー制御に焦点を当てているかを理解するための良いリファレンスになります。

後者の例は、Java/c#langコードと、最初の部分は、Haskellが値(ゼロ)で関数をオーバーロードするのとは対照的に、言語自体の制限であり、したがって純粋な関数型言語ではないと言うことができますあなたはそれが機能的なプログラムをサポートしていると言うことができます。ある程度。

開示:上記のコードはいずれもテスト/実行されていませんが、概念を伝えるのに十分であることが望まれます。また、そのような修正についてのコメントをお願いします:)

9
old-monk

Functional Programmingは宣言型プログラミングの一種で、計算のロジックを記述し、実行の順序は完全に強調されません。

問題:このクリーチャーを馬からキリンに変えたい。

  • 首を長くする
  • 脚を伸ばす
  • スポットを適用する
  • 生き物に黒い舌を与える
  • 馬の尾を削除

各アイテムを任意の順序で実行して、同じ結果を生成できます。

Imperative Programmingは手続き型です。状態と順序が重要です。

問題:車を駐車したい

  1. ガレージのドアの初期状態に注意してください
  2. 私道で車を止める
  3. ガレージのドアが閉じている場合は、ガレージのドアを開いて、新しい状態を覚えておいてください。そうでなければ続行
  4. 車をガレージに引き込む
  5. ガレージのドアを閉める

目的の結果を得るには、各ステップを実行する必要があります。ガレージのドアが閉じているときにガレージに引っ張ると、ガレージのドアが破損します。

8
Jakub Keller

関数型プログラミングは、「関数を使用したプログラミング」です。関数には、参照透過性を含む、予想される数学的プロパティがあります。これらのプロパティから、さらなるプロパティが流れます。特に、数学的証明につながる代替可能性によって可能になる、馴染みのある推論手順(つまり、結果に対する信頼を正当化する)です。

したがって、機能プログラムは単なる表現にすぎません。

式が参照的に透明ではなくなった(したがって関数と値で構築されず、それ自体が関数の一部になれない)命令型プログラムの場所に注目すると、2つのスタイルのコントラストを簡単に確認できます。最も明白な2つの場所は次のとおりです。突然変異(変数など)他の副作用非ローカル制御フロー(例外など)

関数と値で構成される式としてのプログラムのこのフレームワークには、言語、概念、「機能パターン」、コンビネーター、およびさまざまな型システムと評価アルゴリズムの実用的なパラダイム全体が構築されています。

最も極端な定義では、ほとんどすべての言語(CやJavaを含む)を関数型と呼ぶことができますが、通常は、特に関連する抽象化(クロージャー、不変値、パターンマッチングなどの構文補助など)を持つ言語の用語を予約しています。関数型プログラミングの使用に関する限り、ファンクチンの使用を伴い、副作用なしでコードを構築します。プルーフの作成に使用

4
Romil pawar

2005年から2013年までのWeb開発では、命令型プログラミングスタイルが実践されていました。

命令型プログラミングでは、アプリケーションが行うべきことをステップごとに正確にリストしたコードを書きました。

関数型プログラミングスタイルは、関数を組み合わせる巧妙な方法を通じて抽象化を生成します。

答えには宣言型プログラミングの記述があり、それに関して、宣言型プログラミングは従うべきいくつかの規則を列挙していると言います。次に、アプリケーションに初期状態と呼ばれるものを提供し、それらのルールにアプリケーションの動作を定義させます。

さて、これらの簡単な説明はおそらくあまり意味をなさないので、類推を介して命令型プログラミングと宣言型プログラミングの違いを見てみましょう。

私たちがソフトウェアを構築しているのではなく、生でパイを焼くと想像してください。たぶん私たちは下手なパン屋で、おいしいパイをどうやって焼くべきかわからないでしょう。

ですから私たちの上司は、私たちがレシピとして知っている道順のリストを教えてくれます。

レシピはパイの作り方を教えてくれます。 1つのレシピは、次のような命令型スタイルで記述されています。

  1. 小麦粉1カップを混ぜる
  2. 卵1個追加
  3. 砂糖1カップを加える
  4. 混合物を鍋に注ぐ
  5. パンをオーブンに30分間、華氏350度で入れます。

宣言型レシピは次のことを行います。

小麦粉1カップ、卵1杯、砂糖1カップ-初期状態

ルール

  1. すべてが混ざっている場合は、鍋に入れます。
  2. すべてが混ざっていない場合は、ボウルに入れます。
  3. すべて鍋に入れたら、オーブンに入れます。

したがって、命令型アプローチは、段階的なアプローチによって特徴付けられます。ステップ1から始めて、ステップ2に進みます。

最終的には最終製品になります。このパイを作り、これらの材料を混ぜて鍋に入れてオーブンに入れて、最終製品を得ました。

宣言型の世界では、それとは異なります。宣言型のレシピでは、レシピを2つの部分に分け、変数のようなレシピの初期状態をリストする1つの部分から始めます。したがって、ここでの変数は、材料の量と種類です。

初期状態または初期成分を取得し、それらにいくつかのルールを適用します。

したがって、初期状態を取り、ルバーブストロベリーパイなどを食べる準備ができるまで、これらのルールを何度も繰り返します。

したがって、宣言的なアプローチでは、これらのルールを適切に構成する方法を知る必要があります。

したがって、私たちが材料や状態を調べたいと思うかもしれないルールは、混合されている場合、鍋に入れます。

当初の状態では、原料をまだ混合していないため、これは一致しません。

したがって、ルール2では、それらが混合されていない場合は、ボウルに混ぜます。はい、このルールが適用されます。

状態として混合成分のボウルができました。

次に、その新しい状態を再びルールに適用します。

ルール1では、材料が混合されている場合は鍋に入れますが、ルール1が適用されます。

これで、材料が混合され鍋に入れられたこの新しい状態になりました。ルール1は関連性がなくなり、ルール2は適用されません。

ルール3では、材料が鍋に入っている場合はオーブンに入れます。この新しいルールは、この新しい状態に当てはまります。

そして、おいしいホットAppleパイなど何でもできます。

さて、あなたが私のようであれば、なぜ命令型プログラミングをまだ行わないのか、あなたは考えているかもしれません。意味あり。

単純なフローの場合はもちろんですが、ほとんどのWebアプリケーションには、命令型プログラミング設計では適切にキャプチャできない、より複雑なフローがあります。

宣言的なアプローチでは、単一の変数であるtextInput=“”のようないくつかの初期成分または初期状態があります。

たぶん、テキスト入力は空の文字列として始まります。

この初期状態を取得し、アプリケーションで定義された一連のルールに適用します。

  1. ユーザーがテキストを入力した場合、テキスト入力を更新します。さて、今は当てはまりません。

  2. テンプレートがレンダリングされる場合、ウィジェットを計算します。

  3. TextInputが更新された場合、テンプレートを再レンダリングします。

さて、これはどれも当てはまらないため、プログラムはイベントが発生するのをただ待つだけです。

そのため、ある時点でユーザーがテキスト入力を更新し、ルール番号1を適用する場合があります。

それを“abcd”に更新するかもしれません

したがって、テキストとtextInputの更新を更新しました。ルール番号2は適用されません。ルール入力3は、テキスト入力が更新された場合に発生し、テンプレートを再レンダリングしてから、テンプレートがレンダリングされた場合にルール2に戻ります、ウィジェットを計算します。ウィジェットを計算します。

一般に、プログラマーとして、より宣言的なプログラミング設計を目指して努力します。

命令型はより明確で明白に見えますが、宣言型アプローチは大規模なアプリケーションに非常にうまく適合します。

1
Daniel

関数型プログラミングを命令的な方法で表現することは可能だと思います。

  • オブジェクトの多くの状態チェックとif... else/switchステートメントの使用
  • 非同期性を処理するタイムアウト/待機メカニズム

そのようなアプローチには大きな問題があります:

  • ルール/手順が繰り返されます
  • ステートフルネスは副作用/ミスの可能性を残します

オブジェクトのような関数/メソッドを処理し、ステートレスネスを受け入れる関数型プログラミングは、私が信じるこれらの問題を解決するために生まれました。

使用例:Android、iOS、またはWebアプリのロジックなどのフロントエンドアプリケーション。バックエンドとの通信。

命令型/手続き型コードで関数型プログラミングをシミュレートするときの他の課題:

  • 競合状態
  • イベントの複雑な組み合わせとシーケンス。たとえば、ユーザーは銀行アプリで送金を試みます。ステップ1)以下をすべて並行して行い、すべてが良好な場合にのみ続行しますa)ユーザーがまだ良いかどうかを確認します(詐欺、AML)b)ユーザーが十分な残高があるかどうかを確認しますc)受信者が有効で良いかどうかを確認します(詐欺、 AML)など。ステップ2)転送操作を実行しますステップ3)ユーザーの残高および/または何らかの追跡の更新を表示します。たとえば、RxJavaの場合、コードは簡潔で賢明です。それがなければ、多くのコード、乱雑でエラーが発生しやすいコードがあると想像できます

私はまた、一日の終わりには、機能コードがアセンブリまたはマシンコードに翻訳され、コンパイラによって命令的/手続き的になると信じています。ただし、アセンブリを記述する場合を除き、人間が高レベル/人間が読める言語でコードを記述する場合、関数型プログラミングは、リストされているシナリオのより適切な表現方法です。

0
ericn