148

私は最近関数型プログラミングについて学んでいます (具体的には Haskell ですが、Lisp と Erlang のチュートリアルも経験しています)。概念は非常に啓発的であることがわかりましたが、「副作用なし」の概念の実際的な側面はまだわかりません. それの実際的な利点は何ですか?私は関数型の考え方で考えようとしていますが、簡単な方法で状態を保存する機能がなければ、非常に複雑に見える状況がいくつかあります (私は Haskell のモナドを「簡単」とは考えていません)。

Haskell (または他の純粋関数型言語) を深く学び続ける価値はありますか? 関数型またはステートレス プログラミングは、実際に手続き型よりも生産的ですか? Haskell や別の関数型言語を今後も使用し続ける可能性は高いですか、それとも理解するためだけに学習する必要がありますか?

私は生産性よりもパフォーマンスを重視しています。だから私は主に、手続き型/オブジェクト指向/その他よりも関数型言語の方が生産性が高いかどうかを尋ねています。

4

9 に答える 9

182

関数型プログラミングの概要を読んでください。

ステートレス プログラミングには多くの利点がありますが、その中でも劇的にマルチスレッド化された同時実行コードは重要です。率直に言えば、可変状態はマルチスレッド コードの敵です。値がデフォルトで不変である場合、プログラマーは 1 つのスレッドが 2 つのスレッド間で共有状態の値を変更することを心配する必要がないため、競合状態に関連するマルチスレッド バグのクラス全体が排除されます。競合状態がないため、ロックを使用する理由もありません。そのため、不変性により、デッドロックに関連する別のクラスのバグもすべて排除されます。

これが関数型プログラミングが重要である大きな理由であり、おそらく関数型プログラミングの列車に飛び乗るのに最適な理由です。デバッグの簡素化 (つまり、関数は純粋で、アプリケーションの他の部分の状態を変更しない)、より簡潔で表現力豊かなコード、設計パターンに大きく依存する言語と比較してボイラープレート コードが少ないなど、他にも多くの利点があります。コンパイラはコードをより積極的に最適化できます。

于 2009-05-10T02:20:28.067 に答える
48

プログラムのステートレスな部分が多ければ多いほど、壊れることなく部分をまとめる方法が増えます。ステートレス パラダイムの威力は、ステートレス (または純粋性)自体にあるのではなく、強力で再利用可能な関数を作成し、それらを組み合わせることができる能力にあります。

John Hughes の論文Why Functional Programming Matters (PDF)で、多くの例を含む優れたチュートリアルを見つけることができます。

特に、代数データ型とパターン マッチング (Caml、SML、Haskell) も備えた関数型言語を選択すると、生産性が大幅に向上します。

于 2009-05-10T02:39:37.177 に答える
20

他の多くの回答は、関数型プログラミングのパフォーマンス(並列処理)の側面に焦点を当てています。これは非常に重要であると私は信じています。ただし、生産性について具体的に質問しました。たとえば、命令型パラダイムよりも関数型パラダイムの方が同じことをより速くプログラムできますか。

私は実際に(個人的な経験から)F#でのプログラミングが私がよりよく考える方法と一致していることを発見したので、それはより簡単です。それが最大の違いだと思います。私はF#とC#の両方でプログラミングしましたが、F#では「言語との戦い」がはるかに少なくなっています。F#の詳細について考える必要はありません。これが私が本当に楽しんでいると私が見つけたもののいくつかの例です。

たとえば、F#は静的に型指定されていますが(すべての型はコンパイル時に解決されます)、型推論は使用している型を判断するため、言う必要はありません。そしてそれが理解できない場合、それは自動的にあなたの関数/クラス/何でも一般的なものにします。したがって、ジェネリックを記述する必要はありません。すべて自動です。つまり、問題について考えることに多くの時間を費やし、それを実装する方法が少なくなっていることを意味します。実際、C#に戻ると、この型推論が本当に見逃されていることに気付きます。もう必要がなくなるまで、それがどれほど気が散るのかはわかりません。

また、F#では、ループを作成する代わりに、関数を呼び出します。これは微妙な変更ですが、ループ構造について考える必要がなくなったため、重要です。たとえば、これは何かを通過して一致するコードの一部です(私は何を思い出せません、それはプロジェクトオイラーパズルからのものです):

let matchingFactors =
    factors
    |> Seq.filter (fun x -> largestPalindrome % x = 0)
    |> Seq.map (fun x -> (x, largestPalindrome / x))

C#でフィルターを実行してからマップ(各要素の変換)を実行するのは非常に簡単だと思いますが、より低いレベルで考える必要があります。特に、ループ自体を記述し、独自の明示的なifステートメントなどを用意する必要があります。F#を学んでから、機能的な方法でコーディングする方が簡単であることに気付きました。フィルタリングする場合は「filter」を記述し、マップする場合は実装する代わりに「map」を記述します。それぞれの詳細。

また、F#をocaml、そしておそらく他の関数型言語から分離していると思う|>演算子も大好きです。これはパイプ演算子であり、ある式の出力を別の式の入力に「パイプ」することができます。それはコードを私がもっと考える方法に従わせます。上記のコードスニペットのように、「ファクターシーケンスを取得し、フィルター処理してから、マップする」ということです。これは非常に高度な考え方であり、ループやifステートメントの記述に忙しいため、命令型プログラミング言語では理解できません。それは私が他の言語に行くときはいつも私が最も恋しいことの一つです。

したがって、一般的には、C#とF#の両方でプログラミングできますが、より高いレベルで考えることができるため、F#の方が使いやすいと思います。関数型プログラミング(少なくともF#では)から細部が削除されているため、生産性が向上していると私は主張します。

編集:関数型プログラミング言語での「状態」の例を求めたコメントの1つを見ました。F#は必須に記述できるため、F#で変更可能な状態を設定する方法の直接的な例を次に示します。

let mutable x = 5
for i in 1..10 do
    x <- x + i
于 2009-05-10T04:14:48.103 に答える
15

デバッグに長い時間を費やしたすべての困難なバグを考慮してください。

では、これらのバグのうち、プログラムの 2 つの別個のコンポーネント間の「意図しない相互作用」が原因で発生したバグはいくつあるでしょうか? (ほぼすべてのスレッドのバグは、共有データの書き込みに伴う競合、デッドロックなどの形式をとっています...さらに、グローバル状態に予期しない影響を与えるライブラリ、またはレジストリ/環境の読み取り/書き込みなどを見つけるのが一般的です。 )少なくとも 3 分の 1 の「ハード バグ」がこのカテゴリに分類されると仮定します。

ステートレス/不変/純粋なプログラミングに切り替えると、これらのバグはすべてなくなります。代わりに、いくつかの新しい課題が提示されます(たとえば、さまざまなモジュールが環境と対話する必要がある場合)。ただし、Haskell のような言語では、それらの対話は明示的に型システムに具体化されます。プログラムの残りの部分との相互作用のタイプに関する機能と理由。

これは、「不変性」IMO の大きなメリットです。理想的な世界では、私たち全員が素晴らしい API を設計し、物事が変更可能であっても、効果はローカルで十分に文書化され、「予期しない」相互作用は最小限に抑えられます。現実の世界では、無数の方法でグローバル状態とやり取りする API が多数あり、これらが最も悪質なバグの原因となっています。ステートレスを目指すということは、コンポーネント間の意図しない/暗黙的/舞台裏での相互作用を取り除くことを目指すことです。

于 2009-05-10T05:11:07.703 に答える
8

ステートレス関数の利点の 1 つは、関数の戻り値の事前計算またはキャッシュを許可することです。一部の C コンパイラでさえ、関数をステートレスとして明示的にマークして、最適化可能性を向上させることができます。他の多くの人が指摘しているように、ステートレス関数は並列化がはるかに簡単です。

しかし、効率だけが問題ではありません。純粋な関数は、それに影響を与えるものが明示的に記述されているため、テストとデバッグが容易です。また、関数型言語でプログラミングする場合、(I/O などで) "ダーティ" な関数をできるだけ少なくする習慣が身につきます。このようにステートフルなものを分離することは、それほど機能的でない言語であっても、プログラムを設計するための良い方法です。

関数型言語は「理解」するのに時間がかかる場合があり、そのプロセスを経ていない人に説明するのは困難です。しかし、十分に長い間粘り強く取り組んできたほとんどの人は、最終的に関数型言語をあまり使用しなくても、大騒ぎするだけの価値があることに気づきます。

于 2009-05-10T02:31:04.187 に答える
7

状態がなければ、コードを自動的に並列化するのは非常に簡単です (CPU はますます多くのコアで作られているため、これは非常に重要です)。

于 2009-05-10T02:12:39.613 に答える
5

しばらく前に、このテーマについて投稿を書きました:純粋さの重要性について.

于 2010-01-27T23:25:33.100 に答える