Haskellでグラフを作成してローカル操作を実行する方法を学びたいのですが、問題はHaskellに固有のものではなく、グラフの代わりに二重リンクリストを検討することもできます。
質問:二重リンクリスト(または他の二重リンクまたは循環データ構造)を実装するための慣用的または推奨される方法は何ですか)そして、主に不変のデータ構造(Haskell、Clojureなど)をサポートし、擁護する言語での操作?特に、言語によって正式に禁止されているインプレース更新を使用する方法は?
二重にリンクされたリストでローカル操作を実行した場合(アイテムが挿入された場合など)、言語の遅延により、リスト全体をすぐにコピーする必要がない場合があることは容易に想像できます。ただし、リストは二重にリンクされているため、1か所で変更された場合、リストの新しいバージョンで古いノードを使用することはできず、遅かれ早かれ、何らかの方法でマーク、コピー、ガベージコレクションを行う必要があります。 。リストの更新されたコピーのみを使用する場合、これらは明らかに冗長な操作ですが、リストのサイズに比例した「オーバーヘッド」が追加されます。
これは、そのようなタスクでは不変データが単に不適切であり、可変データを「ネイティブ」でサポートしていない関数型宣言言語は、命令型言語ほど優れていないことを意味しますか?または、トリッキーな回避策はありますか?
追伸私はインターネットでこの主題に関するいくつかの記事とプレゼンテーションを見つけましたが、それらをフォローするのに苦労しましたが、この質問への答えは1段落以上、そしておそらく図表を取るべきではないと思います...ない場合、この問題の「機能的な」解決策は、おそらく「Cを使用する」ことです。ある場合、それはどれほど複雑になる可能性がありますか?
"関数型プログラミングのデータ構造" 。非効率的な代替手段の代わりにインプレース更新を使用することについての私の特定の質問はそこでは議論されていません。
"永続的なデータ構造の内部変異" 。ここでは、不特定の言語での低レベルの実装に重点が置かれているようですが、私の質問は、言語の適切な選択(関数型またはその他)と、関数型言語で可能な慣用的な解決策についてです。
純粋に関数型のプログラミング言語では、多くのアルゴリズムを非常に簡潔に表現できますが、その場で更新可能な状態が重要な役割を果たすと思われるアルゴリズムがいくつかあります。これらのアルゴリズムでは、更新可能な状態がない純粋に関数型の言語は本質的に非効率的です( [Ponder、McGeer and Ng、1988] )。
-John LaunchburyおよびSimon Peyton Jones、 Lazy function state threads (1994)、John LaunchburyおよびSimon Peyton Jones、 Haskellの状態 (1995)。これらの論文では、Haskellの ST
モナディック型コンストラクタを紹介しています。
特定のタスクに適合する他の効率的な不変のデータ構造が存在する可能性がありますが、二重リンクリストほど一般的ではありません(残念ながら その可変性により同時変更バグが発生しやすい ) )。問題をより厳密に指定すると、おそらくそのような構造が見つかります。
不変構造の(比較的)経済的トラバースの一般的な答えは、lensesです。変更された不変構造を再構築するのに十分な情報を保持できるという考え方です。変更されていない部分と現在変更されている部分を確認し、隣接するノードに移動します。
別の便利な構造は、 zipper です。 (面白い部分は、 レンズ ジッパーは、構造の型シグニチャーの学校数学の派生物です。)
ここにいくつかのリンクがあります。
Haskellは可変データ構造の使用を妨げません。それらを使用するコードの一部は最終的にIOアクションを返す必要があるため、最終的にIOアクション(メイン関数によって返されます)が、本当に必要な場合にそのような構造を使用することを不可能にしません。
今後の方法として、ソフトウェアトランザクションメモリの使用を調査することをお勧めします。可変構造を実装するための効率的な方法を提供するだけでなく、スレッドセーフの非常に有用な保証も提供します。 https://hackage.haskell.org/package/stm にあるモジュールの説明と https://wiki.haskell.org/Software_transactional_memory にあるWikiの概要を参照してください。