如何在Scala中初始化和"修改"循环持久数据结构?

men*_*ics 12 scala immutability cyclic-reference cyclic-graph

我搜索并找到了有关此主题的一些信息,但答案要么令人困惑,要么不适用.

我有这样的事情:

class Thing (val name:String, val refs:IndexedSeq[Ref])
class Ref (val name:String, val thing:Thing)
Run Code Online (Sandbox Code Playgroud)

现在,我想说,加载一个文件,解析它并从中填充这个数据结构.它是不可变的和循环的,怎么会这样做?

另外,假设我确实填充了这个数据结构,现在我想修改它,比如更改rootThing.refs(3).name,怎么可能这样做呢?


感谢此处发布的想法.在这一点上,我在想如果一个人真的想要这样的东西的持久性数据结构,那就跳出框框思考并考虑客户端代码需要问什么问题.因此,不要考虑对象和字段,而应考虑查询,索引等.首先,我在想: 是否存在双向多图持久数据结构?

Mil*_*bin 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
Run Code Online (Sandbox Code Playgroud)

但是,你不能使它持久化:循环,任何变化都可以通过结构的每个元素看到,因此没有机会在版本之间共享.


0__*_*0__ 5

对于单个循环引用,您可以使用lazy:

lazy val t: Thing = new Thing("thing", Vector(new Ref("ref", t)))
Run Code Online (Sandbox Code Playgroud)

然而,显然这与多对多连接变得复杂.

我不知道是否存在通用的纯函数循环图数据结构.使用非循环图表,这很容易,因为您可以在拓扑上对其进行排序,然后逐步对其进行初始化.

也许使用间接是一个选项,比如通过标识符而不是实际的scala对象引用来引用对象?

case class ThingByID(id: Int, name: String, refs: IndexedSeq[RefByID])
case class RefByID(name: String, thingID: Int)
Run Code Online (Sandbox Code Playgroud)

然后你可以在加载你的文件后通过他们的ID收集东西到一个不可变的地图(例如collection.immutable.IntMap),并在来自ref时查找它们.

编辑

迈尔斯对于第一例是正确的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)))
Run Code Online (Sandbox Code Playgroud)


Ben*_*son 5

还有一种替代方法需要重新思考如何表示对象关联:而不是在对象内部的对象之间存储关联(通常在OO代码中完成),然后将它们作为地图添加到单独的层中.

由于在创建关联时存在对象图中的所有节点,因此可以使用Maps轻松创建不可变的bidrectional关联.

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)
Run Code Online (Sandbox Code Playgroud)

如果你考虑一下,这种方法与关系数据库的工作方式类似.表之间的多值关联不存储在行本身中,而是存储在单独的索引中.即使关联索引不存在,因此使用表扫描解析查询,它也会使用主键索引来查找结果的所有候选行.

这种风格的一个优点是可以在不影响对象本身的情况下添加或更改关联,并且可以针对不同的受众/用例使用不同的关联.缺点是在关联开始的实例上不能直接访问关联映射; 它必须传递并单独提供.