不変状態でオブジェクトグラフの突然変異を効率的に表現することは可能ですか?


12

私はC ++で不変オブジェクトの使用を練習しています。私の個人的な目標は、一連の不変グラフで(ヒープ内の)汎用オブジェクトグラフを表現することです。

マルチバージョングラフ自体の作​​成はそれほど難しくありません。問題はパフォーマンスです。ブルートフォースバージョン管理にはグラフの完全なコピーが必要であり、これは受け入れられませんでした。

変更されていないノードを共有しようとしました。しかし、この場合、新しい問題が発生しました。参照。他のオブジェクトへの参照は、グラフ全体で更新する必要があります。これは、新しいグラフバージョンを導出するたびにすべてのノードを訪問する必要があります。また、これによりノードが参照で変更されるため、ノードも(コピーすることによって)派生する必要があります。総当たりコピーよりもパフォーマンスはそれほど良くありません。

私が想像できる限り、オブジェクトグラフの変化を不変状態で表現するための実際の効率的な方法はありません。だから私はこれに関するいくつかのアイデアを求めています。

不変状態でオブジェクトグラフの突然変異を効率的に表現することは可能ですか?


1
ノードにエッジを配置しているため、難しいだけです。エッジを不変のコレクションに外部に保存すると、簡単になります。
dan_waterworth

@dan_waterworth隣接リストを使用する場合、各エッジを毎回検索する必要があります。したがって、読み取りと書き込みのパフォーマンスのトレードオフだと思います。
エオニル

1
リストである必要はありません。
dan_waterworth

@dan_waterworth Bツリーのようなものですか?
エオニル

2
Bツリーなどの幅広いツリーは、ストレージメディアへのレイテンシが大きいときに使用される傾向があります。この場合、より狭いものを使用する方が良いかもしれませんが、はい、ある種のバランスのとれた検索ツリーは良い考えです。
dan_waterworth

回答:


11

探しているものは、永続データ構造と呼ばれます。永続的なデータ構造の標準リソースは、Chris OkasakiのBook Purely Functional Data Structuresです。ClojureとScalaでの普及により、永続的なデータ構造が最近注目を集めています。

ただし、奇妙な理由により、永続グラフはほとんど無視されているようです。リスト、さまざまな種類のツリー、配列、優先度キュー、マップがありますが、グラフはありません。

本当に見つけた論文は1つだけです。完全永続グラフ-どちらを選ぶべきですか?


4

オブジェクト間の接続をバージョン管理されたリソースの一部と見なさない場合(そして、この場合、おそらく以下はあまり役に立たない可能性があります)、オブジェクトを接続部分を表す部分に分割することを検討できますオブジェクトと不変状態を表す部分の。

次に、接続性サブオブジェクトのそれぞれに、バージョン管理された状態のベクトルを含めることができます。このように、グラフのバージョン番号を操作して、適切な不変状態にアクセスできます。

特定のノードに更新があるたびにグラフ全体を走査する必要を回避するために、ノードの現在のバージョン番号よりも大きいバージョン番号でノードにアクセスした場合、現在のバージョンが使用されるようにできます。その後、ノードが更新されると、すべての中間バージョンに以前のバージョンを入力するため、オブジェクトグラフの遅延更新を実行できます。

オブジェクト間の接続がバージョン管理された状態の一部である場合、上記は機能しません。ただし、次のように拡張できます。

グラフ内のオブジェクトごとに、「ハンドルオブジェクト」を作成します。ハンドルオブジェクトには、バージョン対応の不変状態のリストが含まれています。グラフのオブジェクトにオブジェクト参照を保存するのではなく、ハンドルオブジェクトへの参照を保存します。その後、オブジェクトへの各参照は、現在処理中のオブジェクトグラフのハンドルとビューのバージョン番号を使用して、ハンドルを介して逆参照されます。これにより、オブジェクトの正しい不変状態が返されます。不変の状態では、ハンドルを使用してグラフ内の他のオブジェクトを参照するため、処理するグラフのバージョンの一貫した日付を常に取得できます。

上記の同じロジックが、ハンドル内のバージョンの更新に適用されます。これにより、遅延したローカライズされた更新が可能になります。


3

非常に良い償却時間の複雑さを伴うこの問題に対する公開された解決策がありますが、何を探すべきか正確にわからないとき見つけるのは困難です。これらの講義ノートで素晴らしい要約を見つけることができます(セクション2.2.3を確認してください)か、元の論文Makeing Data Structures Persistentをご覧ください。オブジェクトグラフの接続が制限されている場合(たとえば、ツリーのような構造)、不変グラフの読み取りと更新の両方で償却済みO(1)複雑性に到達することもできます。

各オブジェクトは、現在の不変の状態を保存する以外に、変更を記録するためのスペースを確保するという考え方です。変更を適用して既存の不変グラフから新しい不変グラフを作成する場合、次の2つのケースを考慮する必要があります。

  • 変更するオブジェクトには、まだ変更のためのスペースがあります。変更をオブジェクトに直接保存できます。

  • オブジェクトには変更用のスペースがありません。現在の値と空の変更レコードに基づいて新しいインスタンスを作成します。次に、古いオブジェクトを参照しているすべてのオブジェクトを再帰的に更新して、新しいオブジェクトを参照する必要があります。

    参照元オブジェクト自体に変更のためのスペースがまだある場合は、参照に変更を直接保存できます。それ以外の場合は、再帰的にカスケードします。

このスキームは、新しい世代の不変オブジェクトグラフの効率的な作成をサポートしますが、不変オブジェクトからデータを読み取るときにアクセスする「バージョン」を指定する必要があるため、読み取りが複雑になります。これは、変更レコードが格納されているために、不変オブジェクトに複数のバージョンの情報が含まれている可能性があるため、確認するバージョンを指定する必要があるためです。

これを実装するとき、APIでこの複雑さを隠そうとします。外部から不変グラフの何かを参照するとき、直接参照の代わりに生成されたスタブを使用するため、呼び出し元は手動で目的のバージョンを渡し続ける必要がありません。これにより、参照は直接ポインターよりもわずかに高価になりますが、便利な価値があると思います。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.