リストが関数型言語で選択されるデータ構造であるのはなぜですか?


45

ほとんどの関数型言語は、リンクリストを主要な不変データ構造として使用します。なぜ木ではなくリストなのか?ツリーはパスを再利用したり、リストをモデル化することもできます。



10
@gnat-これはその質問の複製ではありません。この質問への背景の一部として指摘された答えを「不変リンクリストが更新され、元のリストの間に尾を共有することができますので、」その質問に受け入れられ、正解は...本質的である
ジュール・

9
明確化の1つのポイントは、プログラミング言語の仕様がその実装とは異なるように、「リスト」(抽象的なプログラミング概念)は「リンクリスト」(その概念の特定の実装)とは異なることです。「関数型言語がリスト(抽象的なプログラミング概念)を主なデータ構造として使用する理由は?」は微妙ですが、「機能言語X、Y、およびZの一般的な実装がリンクリストをメインデータ構造として使用する理由」という質問とは根本的に大きく異なります。
RM

私はscala Vector(これはツリーとして実装されています)はListより少し優先されますstackoverflow.com/questions/6928327/…実際には、ほとんどの人はまだリスト(私が見たもの)を使用しています。
アカヴァル

Scalaで関数型プログラミングコースをいくつか受講し、ツリーでLOTを使用しました。例としては、リストが単純なだけです。ツリーはパフォーマンスの問題に使用されます。不変リストに要素を追加するのと同じように、古いツリーの一部を再利用して不変ツリーを作成できます。
ボルジャブ

回答:


56

リストはツリーよりも単純だからです。(リストが縮退ツリーであり、すべてのノードに子が1つしかないという事実から、これを簡単に見ることができます。)

短所リストは、任意のサイズの可能な限り単純な再帰的データ構造です。

Guy Steeleは、Fortressプログラミング言語の設計中に、将来の超並列計算のために、データ構造と制御フローの両方が、現在のように線形ではなく、複数の分岐を持つツリー型であるべきだと主張しました。しかし、当面の間、コアデータ構造ライブラリのほとんどは、並列処理ではなく、逐次反復処理(または末尾再帰、実際には問題ではなく、同じこと)を念頭に置いて設計されています。

例えば、今日の並列分散「クラウド」世界向けにデータ構造が特別設計されたClojureでは、おそらく最も「線形」なデータ構造である配列(Clojureではベクトルと呼ばれる)でさえ実際に実装されていること注意してください木。

つまり、要するに、短所リストは可能な限り単純で永続的な再帰データ構造であり、より複雑な「デフォルト」を選択する必要はありませんでした。もちろん、その他のオプションもオプションとして利用できます。たとえば、Haskellには配列、優先度キュー、マップ、ヒープ、トレープ、試行など、想像できるすべてのものがありますが、デフォルトは単純なconsリストです。


10
はい、Clojureベクトルはツリーとして実装されますが、標準のバイナリではなく、ハッシュ配列にマップされた tryであることに注意しくださいdata Tree a = Leaf a | Branch a (Tree a) (Tree a)。これにより、「単純さ」の議論が強化されます。
wchargin

1
FWIW Clojureの永続的ベクトルは、(@ wcharginがやや複雑なことを指摘しているように)高速(大きなベースの対数)アクセスおよび任意の要素の更新のために実装されています。程度)。他のFP言語は、両方を必要とする場合(Haskell SequenceやScala など)に同じ選択を行います(異なる種類のツリーをVector使用することもあります)が、読み取りのみが必要な場合はツリーを使用しないでください。 Haskell Vectorまたは.Net経由のF#ImmutableArray
badcook

1
たとえばpmap、Clojureのベクターに対するpingは、引き続き各要素に順次アクセスします。通常、ベクターのツリー構造はエンドユーザーからは見えません。
badcook

5

実際、これらのリストツリーです!2つのフィールドを持つノードがあり、carおよびにはcdr、そのようなノードまたはリーフをさらに含めることができます。それらのツリーをリストにする唯一のことは、リンクを線形リスト内の次のノードへのリンクとして解釈しcdrリンクをcar現在のノードの値として解釈する規則です。

そうは言っても、関数型プログラミングにおけるリンクリストの普及率は、反復を超える再帰の普及率にリンクしていると思います。手元にあるループ構造のみが(末尾)再帰である場合、それで使いやすいデータ構造が必要です。リンクリストはそのために最適です。


5
それは言語に依存します。確かに、コンスセルを使用してLISPのようなツリーを実装できますが、たとえばHaskellでは、完全に独立した構造が必要になります。また、ほとんどの関数型言語には末尾再帰よりも多くのループ構造があります。たとえば、Haskellコアライブラリは、フォールド(左と右)、スキャン、ツリーのトラバーサル、マップのキーと値の反復、およびその他の多くの専門的な構造を提供します。確かに、それらは舞台裏で再帰で実装されていますが、ユーザーはそれらを動作させるためにそれについて考える必要さえありません。
ジュール

@Julesそれでも、関数型プログラミングは開発され、LISPなどの言語の影響を強く受けました。明らかに、フードの下で配列を使用することにより、このすべてのリスト反復をより効率的にするか、リストの性質を隠す構文糖を追加することができますが、純粋な関数型プログラミングはそれが可能です。また、Haskellはごく最近の言語(LISPより32年若い)であり、純粋な機能スタイルに他の多くのアイデア、構文糖衣などを追加します。私は... Haskellは少しTHERMOMIXを判断してミキサーを判断するようなもので判断することにより、関数型プログラミングを判断し、考える
cmaster

2
@cmasterはHaskellの方が若いですが、まだ27歳であり、多くの言語に影響を与えています(Python、Java、C#など、影響力の少ない名前を付けました)。LISPによる関数型プログラミングの判断は、ALGOLによる命令型プログラミングの判断に似ています-両方とも認識できなくなるよりも長い道のりを歩んでいます。OK。Lispは間違いなく関連性がありますが、私はそれを「主流」とは思わず、ML型を含む後の多くの洞察を逃します。
マチェイピエチョトカ

1
ツリー全体をタッチしたい場合、明示的にスタックせずに、単独でリンクされたツリーのテールを再帰的または反復的に処理することはできません。したがって、テール再帰はそれとは関係ないと思います。
K_G

@MaciejPiechotka Python、Java、C#のいずれも関数型言語と見なすことはできません。それらは必須であり、関数型プログラミングに触発されたいくつかの機能を追加します。状態が変化すると、関数型プログラミングではなく命令型の領域にしっかりと入ります。ただし、LISPは間違いなく主流ではないことに同意します。
cmaster
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.