4

不変性が常に聖杯であるとは限らないことは承知しています。しかし、私はかなり長い間 Scala を学んでいるので、特に純粋な「データ オブジェクト」に関しては、最初は常に不変の解決策を見つけようとします。現在、特定のシナリオで不変オブジェクト グラフを作成する方法を探していますが、これが可能かどうかはわかりません。

一度グラフを作成したいだけで、作成の変更は必要ありません。

次のシナリオを想像してください。

  • タイプは 1 つだけですPerson
  • Personオブジェクトは、次の 2 種類の参照を持つことができます。
    • Person と潜在的な子 (タイプも ) の間には、一方向の 1 対 n の関係がありPersonます。
    • さらに、妻には夫がいて、妻には夫がいます。

最初の問題は、2 人の配偶者の関係が循環的であることです。参照を設定すると (不変性により) 新しいオブジェクトが生成されるため、最終的に配偶者 A は配偶者 B_old を指し、配偶者 B は配偶者 A_old を指すことになります。別の投稿の誰かが、循環参照と不変性は矛盾した表現だと言いました。配偶者Aが独自のコンストラクターで配偶者Bを作成して渡すことができるため、これが常に当てはまるとは思いませんがthis、この不快なアプローチを使用した場合でも、後で子参照を追加すると、AとBが再び変更されます。逆に、子供から始めて配偶者をつなぐと、同様の状況になります。

現時点では、これを行う方法はないと思います。しかし、私が間違っている可能性があり、私が認識していないパターンや回避策がいくつかあります。そうでない場合、可変性が唯一の解決策ですか?

4

2 に答える 2

4

不変のサイクルを作成する方法をいくつか想像できますが、これらに限定されません。

  • 非公開で変更可能なクラス。外部から事実上不変です
  • 反射

しかし、私が最も気に入っているもの (そしてそれは本当にスケーラウェイです) は、慎重に遅延評価と名前によるパラメーターを組み合わせたものです。

object DeferredCycle extends App {

  class C(val name:String, _child: => C) {
    lazy val child = _child
    override def toString: String = name + "->" + child.name
  }

  val a:C = new C("A", b)
  val b:C = new C("B", a)

  println(a)
  println(b)
}

版画:

A->B
B->A
于 2015-10-22T22:50:55.387 に答える
2

別のパースペクティブを追加するために、常にリレーションを包含としてモデル化する必要はありません。不透明な識別子など、別のレベルの間接性を追加できます。

case class PersonId(id: Int)
case class Person(id: PersonId, name: String, spouse: Option[PersonId], children: Seq[PersonId])

val people: Map[PersonId, Person] = ...

あるいは、リレーションは のメンバーである必要さえなく、Person外部で維持することもできます:

case class PersonId(id: Int)
case class Person(id: PersonId, name: String)

val people: Map[PersonId, Person] = ...
val spouses: Map[PersonId, PersonId] = ...
val children: Map[PersonId, Seq[PersonId]] = ...
于 2015-10-23T01:30:08.420 に答える