web-dev-qa-db-ja.com

純粋に機能的なデータ構造の利点は何ですか?

データ構造に関するテキストは多数あり、データ構造コードのライブラリもあります。純粋に機能的なデータ構造の方が推論しやすいことを理解しています。ただし、命令型の対応物よりも、実用的なコードで(関数型プログラミング言語を使用するかどうかに関係なく)純粋に機能的なデータ構造を使用することの実際の利点を理解するのは困難です。純粋に機能的なデータ構造に利点がある実際のケースを誰かが提供できますか?その理由は何ですか?

私が使用するような例data_structure_name in programming_language to do applicationなぜなら、それはcertain_thingを実行できるからです。

ありがとう。

PS:純粋に機能的なデータ構造とは、永続的なデータ構造と同じではありません。永続データ構造は変わらないデータ構造ですか?一方、純粋に機能的なデータ構造は、純粋に動作するデータ構造です。

56
leon

純粋に機能的な(永続的または不変の)データ構造には、いくつかの利点があります。

  • それらをロックする必要はありません。これにより、同時実行性が大幅に向上します。
  • それらは構造を共有でき、メモリ使用量を削減します。たとえば、Haskellのリスト[1、2、3、4]やJavaなどの命令型言語について考えてみます。 Haskellで新しいリストを作成するには、新しいcons(値と次の要素への参照のペア)を作成し、それを前のリストに接続するだけです。 Javaでは、前のリストを損傷しないように、完全に新しいリストを作成する必要があります。
  • 永続データ構造を作成できますlazy
  • また、関数型スタイルを使用すると、時間と操作の順序を考える必要がなくなり、プログラムをより多くすることができます 宣言型
  • 実際、データ構造は不変であるため、さらにいくつかの仮定を行うことができるため、言語の機能を拡張できます。たとえば、 Clojure は、不変性の事実を使用して、各オブジェクトにhashCode()メソッドの実装を正しく提供するため、任意のオブジェクトをマップのキーとして使用できます。
  • 不変のデータと関数型スタイルを使用すると、自由に使用することもできますメモ化

より多くの利点があります。一般に、これは実世界をモデル化する別の方法です。 This およびSICPの他のいくつかの章では、不変の構造を使用したプログラミング、その長所と短所をより正確に把握できます。

71
ffriend

共有メモリの安全性に加えて、最も純粋に機能するデータ構造は、永続性を提供し、実質的に無料です。たとえば、OCamlにsetがあり、それにいくつかの新しい値を追加したいとします。これを行うことができます。

module CharSet = Set.Make(Char)
let a = List.fold_right CharSet.add ['a';'b';'c';'d'] CharSet.empty in
let b = List.fold_right CharSet.add ['e';'f';'g';'h'] a in
...

aは、新しい文字を追加した後もunmodifiedのままです(広告のみが含まれます)が、bにはahが含まれ、それらは同じメモリの一部を共有します(setを使用すると、AVLツリーであり、ツリーの形状が変わるため、共有されるメモリの量を判断するのは難しいです)。ツリーに加えたすべての変更を追跡して、以前の状態に戻ることができるようにして、これを続行できます。

これは 純粋関数に関するウィキペディアの記事 からのすばらしい図で、文字「e」を二分木xsに挿入した結果を示しています。

alt text

24
Niki Yoshiuchi

Erlangプログラムは、ほぼ排他的に関数型データ構造を使用し、複数のコアにほぼシームレスにスケーリングすることで大きなメリットを享受します。共有データ(主にバイナリとビット文字列)は変更されないため、そのようなデータをロックする必要はありません。

14
Marcelo Cantos

F#のこの小さなスニペットを取ります:

let numbers = [1; 2; 3; 4; 5]

これは1から5までの整数の不変リストであると100%確実に言うことができます。そのリストへの参照を渡すことができ、リストが変更されている可能性があることを心配する必要はありません。それは私がそれを使う十分な理由です。

9
ChaosPandion

純粋に機能的なデータ構造には、次の利点があります。

  • 永続性:古いバージョンは、変更できなかったため、安全に再利用できます。

  • 共有:データ構造の多くのバージョンは、適度なメモリ要件で同時に保持できます。

  • スレッドセーフ:ミューテーションはレイジーサンク(存在する場合)内に隠されているため、言語実装によって処理されます。

  • シンプルさ:状態変化を追跡する必要がないため、特に同時実行のコンテキストで、純粋に機能的なデータ構造を簡単に使用できます。

  • 増分:純粋に機能的なデータ構造は多くの小さな部分で構成されているため、増分ガベージコレクションに最適であり、レイテンシが低くなります。

純粋に関数型のデータ構造の利点として並列処理をリストしていないことに注意してください。これが当てはまるとは思わないからです。効率的なマルチコア並列処理には、キャッシュを活用し、メインメモリへの共有アクセスのボトルネックを回避するために予測可能な局所性が必要であり、純粋に機能的なデータ構造には、この点に関してせいぜい未知の特性があります。その結果、純粋に機能的なデータ構造を使用する多くのプログラムは、すべての時間をキャッシュミスに費やし、共有メモリパスウェイを争うため、マルチコアで並列化すると拡張性が低下します。

純粋に機能的なデータ構造とは、永続的なデータ構造と同じではありません。

ここにはいくつかの混乱があります。純粋に機能的なデータ構造のコンテキストでは、永続性とは、データ構造の以前のバージョンがまだ有効であるという知識の中で安全に参照できることを指すために使用される用語です。これは純粋に機能することの自然な結果であり、したがって、永続性はすべての純粋に機能するデータ構造に固有の特性です。

8
Jon Harrop