我正在尝试了解trie等不可变性的实现方式,这与JS中的不可变性有关。我了解应该如何进行重要的结构共享。
我的问题是说你有一个像这样的图结构:
a -- b
|
c
|
d -- h
|
e -- i -- l
|
f -- j -- m
|
g -- k -- n
因此,您将x
添加到系统中。我将尝试两种不同的方式:
a -- b
|
c
|
d -- h -- x
|
e -- i -- l
|
f -- j -- m
|
g -- k -- n
那只是被添加为一个叶子节点。
a -- b
|
c
|
d -- h
|
x
|
e -- i -- l
|
f -- j -- m
|
g -- k -- n
将其添加到路径的中间。
我想知道不变的数据结构将如何处理这两种情况。因此,从本质上讲,我们具有一个函数f : graph -> graph'
,可将图形更改为“新图形”,而在幕后,它仅需对数据结构进行少量调整。不确定其外观或工作方式。我的第一个解释尝试是这样的……
它始于包装对象,就像JS对象顶部的ImmutableJS的API层。
--------------------------
| |
| a -- b |
| | |
| c |
| | |
| d -- h |
| | |
| e -- i -- l |
| | |
| f -- j -- m |
| | |
| g -- k -- n |
| |
--------------------------
然后您进行更改,它会创建一个 new 包装器对象。
-------------------------- --------------------------
| | | |
| a -- b | | |
| | | | |
| c | | |
| | | | |
| d -- h --------------------------------- x |
| | | | |
| e -- i -- l | | |
| | | | |
| f -- j -- m | | |
| | | | |
| g -- k -- n | | |
| | | |
-------------------------- --------------------------
然后对于第二个示例同样如此:
-------------------------- --------------------------
| | | |
| a -- b | | |
| | | | |
| c | | |
| | | | |
| d -- h | | |
| | | | |
| o --------------------------------- x |
| | | | |
| e -- i -- l | | |
| | | | |
| f -- j -- m | | |
| | | | |
| g -- k -- n | | |
| | | |
-------------------------- --------------------------
这些框是您使用的API对象,其中的图形是普通的JS数据对象。
但是在这些示例中,原始图形结构被修改(在第一个示例中放置指向h
的链接,在第二个示例中放置o
占位符)。所以我想知道您将这些东西具体化为不变的情况。我对图形所做的每一次更改都希望“返回一个新对象”,但是在幕后却有最佳的结构共享。
谢谢您的帮助。
答案 0 :(得分:7)
在trie
的情况下的示例不是不变性的通用解决方案,它只是在树中表示数组然后对持久树应用通用解决方案的一种方式。
以下是持久图的一般解决方案
Mulplicative slowdown
)。 路径复制
在这种情况下,我们创建一个保留所有子代的新节点,并为它的根路径中的每个节点创建一个新节点。在这种情况下,我们必须存储一个根数组。它的访问时间与原始图相同,只是它需要花费额外的时间是由于在根数组(Additive slowdown
)上进行搜索。这就是trie
示例中使用的内容。由于每次更改都会创建一组具有新根的新节点,这表示从新根到新节点的路径,因此空间效率低下。
修改框(Sleator,Tarjan等)
这一个结合了Fat节点和Path复制。每个节点只能存储一个修改。如果我们尝试更新已修改的节点,那么我们将使用路径复制并尝试创建具有重复路径的重复节点。有趣的是,在创建新路径时,我们必须注意修改框。在新路径中,仅重复那些已经被修改的节点,否则仅更新那里的修改框。
注意:“路径复制和修改”框适用于树(或可能是DAG),而不是通用图。由于这两个过程都涉及从简化节点到根级联创建新节点。通用图没有根。因此,我们唯一可用的方法是用于普通图的胖节点。
参考:
1. https://en.wikipedia.org/wiki/Persistent_data_structure
2. https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-854j-advanced-algorithms-fall-2005/lecture-notes/persistent.pdf
脂肪节点
节点和图的以下结构应足够
Node ->
var value;
Node parent
Node[] children
Modification[] modifications
Modification ->
Node node
Date timestamp
Graph -> (Adjancency list)
{
'a': [b],
'b': [c],
'c': [d],
'd': [h],
'e': [i],
'f': [j],
'g': [k],
'h': [d, i],
'i': [e, j, l],
'j': [f, i, k, m],
'k': [g, j, n],
'l': [i],
'm': [j],
'n': [k],
}
胖节点案例1
胖节点案例2
路径复制
如果您的示例中的图是一个以节点a
为根的树,则路径复制的工作方式与trie
示例中所述的相同
在具有根数组的简单树节点之后就足够了
Node ->
var value
Node parent
Node[] children
Graph ->
roots: [
{
Node root1,
Date timestamp
},
{
Node root2,
Date timestamp
}
...
]
由于修改了节点h
,因此将复制从节点h
到根节点a
的整个路径。
路径复制案例1
路径复制案例2
修改框
假设示例中的图形为树,则只需满足以下条件
Node ->
var value
Node parent
Node[] children
ModificationBox modBox
ModificationBox ->
timestamp,
Attribute {
type: value/parent/children[i] etc (only one attribute)
value: value of attribute
}
Graph ->
roots: [
{
Node root1,
Date timestamp
},
{
Node root2,
Date timestamp
}
...
]
修改框案例1
节点h
未修改
修改框案例2
在这种情况下,假设h
已被修改