Javascript 如何实现不变性

Javascript 如何实现不变性,javascript,graph,immutability,immutable.js,trie,Javascript,Graph,Immutability,Immutable.js,Trie,我试图理解如何实现不变性中的and等,这与JS中的不变性有关。我理解应该如何进行重大的结构共享 我的问题是假设你有一个类似这样的图形结构: a -- b | c | d -- h | e -- i -- l | f -- j -- m | g -- k -- n 因此,您可以向系统添加一个x。我将尝试两种不同的方法: a -- b | c

我试图理解如何实现不变性中的and等,这与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

在路径中间添加了一个。< /P> 我想知道不可变的数据结构将是什么来处理这两种情况。因此,本质上我们有一个函数

f:graph->graph'
,它将图形更改为一个“新图形”,当它处于后台时,它应该只对数据结构进行一个小的调整。不知道这会是什么样子,也不知道它是如何工作的。我第一次尝试解释是这样的

它从一个包装器对象开始,这个包装器对象类似于JS对象顶部的ImmutableJS API层

 --------------------------
|                          |
|    a -- b                |
|         |                |
|         c                |
|         |                |
|         d -- h           |
|              |           |
|         e -- i -- l      |
|              |           |
|         f -- j -- m      |
|              |           |
|         g -- k -- n      |
|                          |
 --------------------------
然后进行更改,它将创建一个新的包装器对象

 --------------------------           --------------------------
|                          |         |                          |
|    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
占位符)。所以我想知道你会如何具体地使这些东西不变。我对图形所做的每一次更改都希望“返回一个新对象”,但在引擎盖下存在最优的结构共享


谢谢您的帮助。

trie的例子并不是不变性的一般解决方案,它只是一种在树中表示数组,然后应用持久性树的一般解决方案的方法

下面是持久图的一般解决方案

  • 脂肪节点
    每个节点存储其更改的历史记录和这些更改的时间戳。在特定时间点查找图形时,我们提供时间戳以获取当时的版本。它节省空间(只存储新值),但在这种情况下,由于对每个节点的修改数组(任意长度)进行额外搜索(
    multiplicative slook
    ),访问时间会受到影响
  • 路径复制
    在本例中,我们创建一个新节点,保留所有子节点,并为其根路径中的每个节点创建一个新节点。在这种情况下,我们必须存储一个根数组。它的访问时间与原始图形相同,只需要在根数组上搜索(
    加法减速
    )。这就是
    trie
    示例中使用的内容。它的空间效率很低,因为每次更改都会创建一组具有新根的新节点,表示从新根到新节点的路径

  • 修改框(Sleator、Tarjan等人)
    这一个结合了Fat节点和路径复制。每个节点只能存储一个修改。如果我们尝试更新一个已经修改的节点,那么我们使用路径复制,并尝试创建一个具有重复路径的重复节点。有趣的是,在创建新路径时,我们必须处理修改框。在新路径中,仅复制已修改的节点,否则仅更新这些修改框

  • 注意:路径复制和修改框应用于树(或可能是DAG),而不是通用图。因为这两者都涉及到从mdoified节点到根节点的新节点的级联创建。泛型图没有根。所以我们唯一可用的方法是泛型图的Fat节点

    参考:
    1.
    2.

    脂肪节点

    下面的节点和图结构应该足够了

    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],
        }
    
    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

    脂肪结节病例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
        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],
        }
    
    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
    已被修改