関数型プログラミングで永続データ構造を使用するのはなぜですか?


22

関数型プログラミングでは、永続的なデータ構造と不変オブジェクトが使用されます。私の質問は、なぜこのようなデータ構造をここに持つことが重要なのですか?データ構造が永続的でない場合、どうなるかを低レベルで理解たいですか?プログラムはより頻繁にクラッシュしますか?


回答:


19

不変データオブジェクトを操作する場合、関数には、同じ入力で呼び出すたびに同じ出力を生成するプロパティがあります。これにより、計算の概念化と正しい計算が容易になります。また、テストが容易になります。

それはほんの始まりです。数学は長い間関数を扱ってきたので、数学から借りることができる多くの推論手法があり、それらをプログラムに関する厳密な推論に使用します。私の観点からの最も重要な利点は、機能プログラムの型システムが十分に開発されていることです。そのため、どこかでミスをすると、型の不一致として現れる可能性が非常に高くなります。したがって、型付き関数型プログラムは、命令型プログラムよりもはるかに信頼性が高い傾向があります。

対照的に、可変データオブジェクトを操作する場合、最初に、計算中にオブジェクトが通過する複数の状態を記憶および管理するという認知的な負荷があります。特定のステップに必要なすべてのプロパティがその時点で満たされるように、正しい順序で物事を行うように注意する必要があります。間違いを犯すのは簡単であり、型システムはそれらの間違いをキャッチするほど強力ではありません。

数学は、変更可能なデータオブジェクトでは機能しませんでした。したがって、それらから借用できる推論手法はありません。コンピューターサイエンスで開発された独自の手法、特にFloyd-Hoare Logicが数多くあります。ただし、これらは標準的な数学的手法よりも習得するのが難しく、ほとんどの生徒はそれらを扱うことができないため、ほとんど教えられません。

2つのパラダイムがどのように異なるかについての簡単な概要については、プログラミング言語の原則に関する私の講義ノートの最初のいくつかの配布資料を参照してください。


これは非常に理にかなっています。PPTを共有していただきありがとうございます。同じもののビデオ録画も共有していますか?
gpuguy

@gpuguy。私はそれほどパワーポイントを使用していません。ホワイトボードは私の好きな媒体です。しかし、配布資料はそれ自体で非常に読みやすいものでなければなりません。
ウダイレディ

+1数学は、変更可能なデータオブジェクトでは機能しませんでした。また、講義ノートへのリンク。
ガイコーダ

15

可変データ構造を使用するよりも、永続データ構造を正しく使用する簡単です。これが主な利点です。

もちろん、理論的に言えば、永続データ構造で行うことはすべて、可変データ構造でも行うことができ、その逆も同様です。多くの場合、永続的なデータ構造は、通常、その一部をコピーする必要があるため、余分なコストがかかります。これらの考慮事項により、スーパーコンピュータのメモリが携帯電話よりも少ない30年前には、永続的なデータ構造の魅力がはるかに低くなりました。しかし、今日では、ソフトウェアの生産における主なボトルネックは、開発時間と保守コストにあるようです。したがって、開発の効率のために、実行の効率をいくらか犠牲にすることをいとわない。

永続的なデータ構造を使用する方が簡単なのはなぜですか?人間は、プログラムの異なる部分間のエイリアシングやその他の種類の予期しない相互作用の追跡が本当に苦手だからです。彼らは自動的に、2つのものがとが呼ばれているので、それから共通点がないxと考えるy。すべての後、「朝の星」と「夜の星」は本当に同じものであると理解するのに努力が必要です。同様に、他のスレッドがデータ構造を処理しているため、またはデータ構造を変更するメソッドを呼び出したため、データ構造が変更される可能性があることを忘れがちです。これらの懸念の多くは、永続的なデータ構造。

永続的なデータ構造には、他の技術的な利点もあります。通常、それらを最適化する方が簡単です。たとえば、必要に応じてクラウド内の他のノードに永続的なデータ構造をいつでも自由にコピーできます。同期の心配はありません。


非常に多くの利点がある場合、命令型言語でも永続データ構造を使用しないのはなぜですか?
gpuguy

4
おそらくすぐに「なぜ命令型言語を使用するのか」と尋ねられるでしょう。
アンドレイバウアー

4
しかし、真剣に、永続的なものに置き換えるのが難しいデータ構造があります。たとえば、配列と行列を使用する数値計算プログラムは、ハードウェアがそのようなものに最適化されているため、従来のデータ構造よりもはるかに高速です。
アンドレイバウアー

1
@gpuguy。永続的データ構造は、適用可能で適切な場合には、命令型言語でも使用できます。これらを使用できるようにするには、言語でガベージコレクションベースのメモリ管理をサポートする必要があります。Javaの、C#、スカラ座やPython、Rubyのは、JavaScriptなど:現代の多くの言語があること持っている
ウダイ・レディ

おそらく、1つの大きな利点は、可変データ構造と比較してより抽象的なインターフェイスです。内部のもの変更できます(不変性と参照整合性を参照)、変更する必要はありません。
ラファエル

2

他の人の答えに加え、数学的アプローチを強化する関数型プログラミングは、リレーショナル代数やガロア接続との相乗効果も優れています。

これは、フォーマルメソッドの分野で非常に役立ちます。
例えば:

  • プログラム検証における形式的証明は、拡張静的チェックにより簡素化されています。
  • リレーショナル代数からの多くのプロパティは、SATの解決に役立ち、合金などのツールがあります。
  • Galois Connectionsを使用すると、Shin-Cheng MuとJoséNuno Oliveiraによる論文を参照しながら、このブログで見られるように、ソフトウェア仕様への計算アプローチが可能になります。
  • ガロア接続(および関数型プログラミング)は、Hoare Logicよりも一般的な概念であるため、Design by Contract形式で使用できます。

{p}P{q}[P]ϕpϕq[P]

  • [P]P
  • ϕpϕq)pq

また、このアプローチにより、最も弱い事前条件最も強力な事後条件の計算が可能になり、多くの状況で役立ちます。


2

データ構造が永続的でない場合はどうなるかを低レベルで理解したいのですが?

データ構造として、巨大な状態空間(状態が2450バイトの「メルセンヌツイスター」など)を持つ擬似乱数ジェネレーターを見てみましょう。乱数を2回以上使用したくないので、これを不変の永続データ構造として実装する理由はほとんどないようです。では、次のコードで何が間違っているのかを自問してみましょう。

mt_gen = CreateMersenneTwisterPRNGen(seed)
integral = MonteCarloIntegral_Bulk(mt_gen) + MonteCarloIntegral_Boundary(mt_gen)

ほとんどのプログラミング言語は順序を指定していないMonteCarloIntegral_BulkMonteCarloIntegral_Boundary評価されます。両方が可変mt_genへの参照を引数としてとる場合、この計算の結果はプラットフォームに依存する可能性があります。さらに悪いことに、異なる実行間で結果がまったく再現できないプラットフォームがあるかもしれません。

一つは、任意の実行のインタリーブようmt_genための効率的な可変データ構造を設計することができるMonteCarloIntegral_BulkMonteCarloIntegral_Boundary「正しい」結果を与えるが、異なる「正しい」結果に一般的なリードで異なるインターリーブします。この非再現性により、対応する機能が「不純」になり、他の問題も発生します。

固定の順次実行順序を強制することにより、非再現性を回避できます。ただし、その場合、コードは、mt_genへの単一の参照のみが常に利用できるように配置できます。型付き関数型プログラミング言語では、一意性型を使用してこの制約を強制することができます。これにより、純粋な関数型プログラミング言語のコンテキストでも安全で変更可能な更新が可能になります。これはすべて素晴らしくダンディに聞こえるかもしれませんが、少なくとも理論的にはモンテカルロシミュレーションは恥ずかしいほど平行です、そして私たちの「ソリューション」はこのプロパティを破壊しました。これは単なる理論上の問題ではなく、非常に現実的な実際的な問題です。ただし、疑似乱数ジェネレーター(生成される機能)と生成する乱数のシーケンスを変更する必要があります。これを自動的に行うプログラミング言語はありません。(もちろん、必要な機能を既に提供している別の擬似乱数ライブラリを使用できます。)

低レベルでは、実行順序がシーケンシャルで固定されていない場合、可変データ構造は容易に非再現性(したがって不純)​​につながります。これらの問題に対処するための典型的な必須の戦略は、可変のデータ構造が変更される実行順序が固定された順次フェーズと、すべての共有される可変データ構造が一定のままである任意の実行順序の並列フェーズを持つことです。


Andrej Bauerは、可変データ構造のエイリアスの問題を提起しました。興味深いことに、FortranやCなどのさまざまな命令型言語では、関数引数のエイリアシングの許容に関する前提が異なります。ほとんどのプログラマーは、言語にエイリアシングモデルがあることをまったく認識していません。

不変性と値のセマンティクスはわずかに過大評価される場合があります。さらに重要なのは、プログラミング言語の型システムと論理フレームワーク(抽象マシンモデル、エイリアシングモデル、同時実行モデル、メモリ管理モデルなど)が「効率的な」データで「安全に」動作するための十分なサポートを提供することです構造。「移動セマンティクス」をC ++ 11に導入することは、理論的な観点からは純度と「安全性」の点で大きな後退のように見えるかもしれませんが、実際には反対です。型システムと言語の論理フレームワークが拡張され、新しいセマンティクスに関連する危険の大きな部分が削除されました。(そして、粗いエッジが残っていても、これは「より良い」によってこれを改善できなかったという意味ではありません


Uday Reddyは、数学が変更可能なデータオブジェクトを扱うことは決してなく、関数型プログラムの型システムは不変のデータオブジェクトに対して十分に開発されているという問題を提起しました。これは、線形論理を動機付けようとするとき、変更可能なオブジェクトを扱うために数学は使用されないというジャンイブジラールの説明を思い出しました。

型システムと関数型プログラミング言語の論理フレームワークを拡張して、「効率的な」可変の非永続データ構造で「安全に」作業できるようにする方法を尋ねるかもしれません。ここでの問題の1つは、古典的な論理とブール代数が、可変データ構造を操作するための最良の論理フレームワークではないかもしれないことです。おそらく、そのタスクには線形論理と可換モノイドがより適しているでしょうか?多分私は、フィリップ・ワドラーが関数型プログラミング言語の型システムとして線形論理について言っていることを読むべきでしょうか?しかし、線形論理でこの問題を解決できなくても、関数型プログラミング言語の型システムと論理フレームワークを「安全」かつ「効率的」に拡張できないというわけではありません


@DWこの答えが独立した答えではないことはおそらく正しいでしょう。現在のところ、Uday ReddyとAndrej Bauerの回答で提起された特定のポイントにのみ拡張されています。スタンドアローンになるように変更して、「データ構造が永続的でない場合はどうなるかを低レベルで理解したい」と直接答えることができると思います。質問の一部。私は、データ構造として巨大な状態空間(2450バイトの状態の「メルセンヌツイスター」など)を持つ擬似乱数ジェネレーターを見て、間違っている可能性のあることを説明します。
トーマスクリンペル14年

@DWこの質問に対する答えが本当に質問に答えているとは思わない。特に、永続的なデータ構造が実際に何であるか(不変であること以外)およびそれらの実装方法についてはあまり何もありません。
ギルデンスターン14年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.