不変データを持つ言語で二重にリンクされたデータ構造または循環データ構造に操作を実装するための回避策


11

Haskellでグラフを作成してローカル操作を行う方法を学びたいのですが、問題はHaskellに固有のものではなく、グラフの代わりに二重リンクリストを検討することもできます。

質問: 主に不変のデータ構造(Haskell、Clojureなど)をサポートおよび推奨する言語で、二重リンクリスト(または他の二重リンクまたは循環データ構造)とその操作を実装する慣用的または推奨される方法は何ですか? ?特に、言語で正式に禁止されているインプレース更新の使用方法は?

二重にリンクされたリストでローカル操作を実行した場合(アイテムが挿入された場合など)、言語の遅延により、リスト全体をすぐにコピーする必要がない場合があることは容易に想像できます。ただし、リストは二重にリンクされているため、1か所で変更された場合、リストの新しいバージョンで古いノードを使用することはできず、何らかの方法でマーク、コピー、ガベージコレクションを行う必要があります。 。リストの更新されたコピーのみを使用する場合、これらは明らかに冗長な操作ですが、リストのサイズに比例した「オーバーヘッド」が追加されます。

これは、そのようなタスクでは不変データが単に不適切であり、可変データを「ネイティブ」でサポートしていない関数型宣言言語は、命令型言語ほど優れていないことを意味しますか?または、トリッキーな回避策はありますか?

PS私はインターネットでこの主題に関するいくつかの記事やプレゼンテーションを見つけましたが、それらをフォローするのに苦労しましたが、この質問への答えは1段落以上、そしておそらく図を取るべきではないと思います...つまり、もしあればこの問題に対する「機能的な」解決策はありません。答えは「Cを使用する」でしょう。ある場合、それはどれほど複雑になる可能性がありますか?


関連する質問

  • 「関数型プログラミングのデータ構造」。非効率的な代替手段の代わりにインプレース更新を使用することについての私の特定の質問はそこでは議論されていません。

  • 「永続的データ構造の内部変異」。ここでは、不特定の言語での低レベルの実装に重点が置かれているようですが、私の質問は、言語(関数型またはその他)の正しい選択と、関数型言語で可能な慣用的な解決策についてです。


関連する引用

純粋に関数型のプログラミング言語では、多くのアルゴリズムを非常に簡潔に表現できますが、その場で更新可能な状態が重要な役割を果たすと思われるアルゴリズムがいくつかあります。これらのアルゴリズムの場合、更新可能な状態がない純粋に関数型の言語は本質的に非効率的であるように見えます([Ponder、McGeer and Ng、1988])。

-John LaunchburyおよびSimon Peyton Jones、レイジー機能状態スレッド(1994)、John LaunchburyおよびSimon Peyton Jones、Haskell州(1995)。これらの論文STでは、Haskellのモナディック型コンストラクタを紹介しています。


4
推奨:岡崎
Robert Harvey

2
参照いただきありがとうございます。私は彼の論文を見つけまし
Alexey

このペーパーは有望に見えます:David KingとJohn LaunchburyによるHaskell(1994)の遅延深さ優先探索と線形グラフアルゴリズム
Alexey

配列に関する同様の問題は、型を実装するdiffarrayパッケージによって対処されるようDiffArrayです。diffarrayパッケージのソースを見ると、91が出てきます。私の質問への答えは「はい、いいえ、不変のデータを持つ純粋に関数型の言語は、通常、インプレース更新に依存するアルゴリズムの実装には適していません」のようです。unsafePerformIO
Alexey

(Haskellでは)私の現在のソリューションは、辞書を(使用しているMapIntMapまたはHashMapストレージとして)とノードがリンクされたノードのIDを含めるようにします。 「コンピューターサイエンスのすべての問題は、別のレベルの間接参照で解決できます。」
Alexey、2016年

回答:


6

特定のタスクに適合する他の効率的な不変のデータ構造が存在する可能性がありますが、二重にリンクされたリストほど一般的ではありません(残念ながら、その可変性により同時変更のバグが発生しやすくなります)。問題をより厳密に指定すると、おそらくそのような構造が見つかるでしょう。

不変の構造の(比較的)経済的な移動に対する一般的な答えはレンズです。 つまり、変更されていない部分と現在変更されている部分から変更された不変構造を再構築し、それを隣接ノードにナビゲートするために十分な情報を保持できるということです。

別の便利な構造はジッパーです。(面白いのは、レンズジッパーのタイプシグネチャが構造のタイプシグネチャの学校数学の派生物であることです。)

ここにいくつかのリンクがあります。


1
必要に応じて、ジッパーも便利かもしれません
jk。

私の問題をより詳細に指定するために、グラフ書き換えシステムをプログラムしたいとします。たとえば、グラフ書き換えに基づくラムダ計算エバリュエーターです。
Alexey

1
@Alexey:グラフの書き換えに関するClean Peopleの作業に精通していますか?wiki.clean.cs.ru.nl/...
ジョルジオ

1
@Alexey:私が知っていることではありません:Cleanは独自に開発されたHaskellのいとこです。また、副作用を処理するための別のメカニズムもあります(AFAIKはユニークタイプと呼ばれています)。一方、開発者はグラフの書き換えで多くの作業を行ってきました。したがって、それらはグラフの書き換えと関数型プログラミングの両方について知っている最高の人々の中にいる可能性があります。
Giorgio

1
私がナビゲートして現在の場所で変更したい場合は、ジッパーが二重にリンクされたリストまたはツリーの問題を解決するようだと同意しますが、いくつかのスポットに焦点を当てたい場合はどうすべきか明確ではありません同時に、たとえば、離れた2つの場所で2つの要素を入れ替えます。「円形」構造で使用できるかどうかはさらに不明確です。
Alexey

2

Haskellは可変データ構造の使用を妨げません。それらを使用するコードの部分は最終的にIOアクション(最終的にはメイン関数によって返されるIOアクションにバインドされる必要がある)を返さなければならないという事実のため、それらは強く非推奨になり、使用が困難になります。あなたが本当にそれらを必要とするならば、そのような構造を使うことを不可能にします。

今後の方法として、ソフトウェアトランザクションメモリの使用を調査することをお勧めします。可変構造を実装するための効率的な方法を提供するだけでなく、スレッドの安全性についても非常に有用な保証を提供します。https://hackage.haskell.org/package/stmにあるモジュールの説明とhttps://wiki.haskell.org/Software_transactional_memoryにあるWikiの概要を参照してください


おかげで、私はSTMについて学びたいと思います。可変性と状態を(私は時につまずきをした持っているHaskellではより多くの方法があるように見えますMVarStateST)私はその違いを把握する必要があるとの使用を意図しますので、。
Alexey、2015

@Alexey:良い点はST、IMOはステートフルな計算を実行し、状態を破棄して結果を純粋な値として抽出できるため、回答で言及する必要があります。
ジョルジオ

@ジョルジオ、STSTMでHaskellを使用して同時実行性と使い捨て状態の両方を持つことは可能ですか?
Alexey

用語の提案がもう1つあります。合成されたメインIOアクションは「メイン関数によって返される」のではなく、main変数に割り当てられます。:)(main関数も保持していません。)
Alexey

あなたの意見はわかりますが、それでも「変数」は、価値を生み出すプロセスではなく、単純な価値としてほとんどの人の心に内包しています。あなたが提案する変更は、明らかに技術的には正しいものの、このテーマに不慣れな人を混乱させる可能性があります。
ジュール、
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.