12

私はこのトピックに関するいくつかの情報を検索して見つけましたが、答えは混乱するか、当てはまらないかのどちらかです。

私はこのようなものを持っています:

class Thing (val name:String, val refs:IndexedSeq[Ref])
class Ref (val name:String, val thing:Thing)

ここで、ファイルをロードして解析し、そこからこのデータ構造を設定します。それは不変で循環的ですが、どのようにそうすることができますか?

また、このデータ構造にデータを入力したとしましょう。rootThing.refs(3).nameを変更するなど、変更したいのですが、どうすればよいでしょうか。


ここに投稿されたアイデアをありがとう。この時点で、このような永続的なデータ構造が本当に必要な場合は、既成概念にとらわれずに考え、クライアントコードがどのような質問をする必要があるかを検討することを考えています。したがって、オブジェクトやフィールドについて考えるのではなく、クエリやインデックスなどについて考えてください。まず、私は次の観点から考えてい ます。双方向のマルチマップ永続データ構造はありますか?

4

4 に答える 4

13

ある程度の怠惰を導入するように変更する準備ができている場合は、この形式の循環データ構造を初期化できます。

scala> class Thing (val name:String, refs0: => IndexedSeq[Ref]) { lazy val refs = refs0 } ; class Ref (val name:String, thing0: => Thing) { lazy val thing = thing0 }
defined class Thing
defined class Ref

scala> val names = Vector("foo", "bar", "baz")                                                                                                                       
names: scala.collection.immutable.Vector[java.lang.String] = Vector(foo, bar, baz)

scala> val rootThing : Thing = new Thing("root", names.map { new Ref(_, rootThing) })
rootThing: Thing = Thing@1f7dab1

scala> rootThing.refs(1).name
res0: String = bar

ただし、永続化することはできません。循環的であるため、構造のすべての要素を介して変更が表示されるため、バージョン間で共有する機会はありません。

于 2011-04-15T22:16:42.277 に答える
5

単一の循環参照の場合、lazyを使用できます。

lazy val t: Thing = new Thing("thing", Vector(new Ref("ref", t)))

ただし、明らかにこれは多対多の接続では複雑になります。

汎用の純粋関数型閉路グラフデータ構造が存在するかどうかはわかりません。非巡回グラフでは、トポロジカルソートを行ってから段階的に初期化できるため、これは簡単です。

たぶん、間接参照を使用することはあなたのためのオプションです、例えば、実際のscalaオブジェクト参照の代わりに識別子を通してオブジェクトを参照するのですか?

case class ThingByID(id: Int, name: String, refs: IndexedSeq[RefByID])
case class RefByID(name: String, thingID: Int)

次に、ファイルをロードした後、IDによって不変のマップ(たとえばcollection.immutable.IntMap)に物を収集し、参照から取得するときにそれらを検索できます。

編集

マイルは、の最初のケースについて正しいlazy val tです。確かに、彼の答えのように名前によるパラメータが必要です。

class Thing(val name: String, val refs: IndexedSeq[Ref])
class Ref(val name: String, _thing: => Thing) { def thing = _thing }

val t: Thing = new Thing("thing", Vector(new Ref("ref", t)))
于 2011-04-15T22:20:12.020 に答える
5

オブジェクトの関連付けがどのように表されるかを再考する必要がある別のアプローチがあります。オブジェクト自体の内部にオブジェクト間の関連付けを格納する代わりに(通常はオブジェクト指向コードで行われるように)、後でマップとして別のレイヤーに追加します。

オブジェクトグラフのすべてのノードは、関連付けが作成されるまでに存在するため、マップを使用して不変の両方向の関連付けを簡単に作成できます。

scala> class Thing (val name:String)
defined class Thing

scala> class Ref (val name:String)
defined class Ref

scala> new Thing("Thing1")
res0: Thing = Thing@5c2bae98

scala> new Ref("Ref1")
res1: Ref = Ref@7656acfa       

scala> val thing2Ref = Map(res0 -> res1)
thing2Ref: scala.collection.immutable.Map[Thing,Ref] = Map(Thing@5c2bae98 -> Ref
@7656acfa)

scala> val ref2Thing = Map(res1 -> res0)
ref2Thing: scala.collection.immutable.Map[Ref,Thing] = Map(Ref@7656acfa -> Thing
@5c2bae98)

あなたがそれについて考えるならば、このアプローチはリレーショナルデータベースがどのように機能するかに似ています。テーブル間の複数値の関連付けは、行自体ではなく、個別のインデックスに格納されます。アソシエーションインデックスが存在せず、テーブルスキャンを使用してクエリが解決される場合でも、主キーインデックスを使用して、結果のすべての候補行を検索します。

このスタイルの利点は、オブジェクト自体に影響を与えることなく関連付けを追加または変更できることと、さまざまなオーディエンス/ユースケースにさまざまな関連付けを使用できることです。欠点は、アソシエーションが開始されるインスタンスではアソシエーションマップに直接アクセスできないことです。渡して個別に提供する必要があります。

于 2011-10-06T13:04:13.200 に答える
3

不変のデータ構造は、コンストラクターによって完全に初期化できます。または、プロパティを変更するときに構造をコピーし続ける必要性を受け入れることができます。したがって、質問の最初の部分に答えるには、データ内のすべての情報を受け入れるコンストラクターを定義することによって不変のデータ構造にデータをロードするか、循環サブグラフを認識していることを確認します。

循環データ構造は必ずしも完全に循環的ではないと思います。単一のインスタンス/状態が保持するポインターのネットワークを想像すると、相互にポイントする親と子を含むサブグラフを持つことができますが、他の循環構造はありません。このシナリオでは、インスタンス1をコピーして別の親ノードを持つインスタンス2を遅延作成する場合(たとえば)、親ノードと子ノードは循環サブグラフを形成するため、コピーする必要があります。ただし、親以外の子内に保持されている参照は、最初のインスタンスと同じ不変の構造への参照であり続けることができます。

たとえば、私のクラスの家には、ドア、窓、屋根への参照があります。ドアには色があり、toHouseは家を参照しています。窓にはサイズがあり、屋根にはピッチがあります。そこで、緑のドア、大きな窓、平らな屋根のあるbobsHouseを作成します。実際、これらはすべて不変であるため、理論的には1つの大きな窓しかありません。大きな窓のある家はすべて同じ窓を持っています。2番目のインスタンスjanesHouseは、bobsHouseと同じですが、切妻屋根があります。したがって、janesHouse = bobsHouse.withRoof(gabled)と言うと、新しい(これも緑色の)ドアと新しい(切妻屋根)の屋根が同じウィンドウである、Houseの新しいインスタンスを取得する必要があります。

したがって、janesHouseが怠惰に評価された場合、ドアまたは屋根が参照されている場合にのみ、新しい家を作成する必要があります。janesHouse.Windowが要求された場合、新しいHouseを作成する必要はまったくありません。必要なのはbobsHouseだけです。

tl; dr:永続的な(遅延した)循環データ構造を持つことができますが、その中に非循環サブグラフが見つかる場合、つまりチェーンではない場合に限ります。

于 2011-04-16T22:15:37.727 に答える