Algorithm 为什么Eric Lippert';s不可变二叉树?

Algorithm 为什么Eric Lippert';s不可变二叉树?,algorithm,data-structures,tree,immutability,Algorithm,Data Structures,Tree,Immutability,我刚才在看Eric Lippert的简单实现,我有一个问题。在展示了实现之后,Eric声明 请注意 不变的数据结构就是它 不可能意外(或 创建一棵 包含一个循环 Eric实现的这一特性似乎不仅仅来自不变性,还来自树是由叶子构建的事实。这自然会阻止节点将其任何祖先作为子节点。如果你在另一个方向上构建树,你会引入循环的可能性 我的想法是对的,还是循环的不可能性仅仅来自不变性?考虑到来源,我想知道我是否遗漏了什么 编辑:在仔细考虑之后,似乎从叶子上建立起来可能是创建不可变树的唯一方法。我说得对吗 如果

我刚才在看Eric Lippert的简单实现,我有一个问题。在展示了实现之后,Eric声明

请注意 不变的数据结构就是它 不可能意外(或 创建一棵 包含一个循环

Eric实现的这一特性似乎不仅仅来自不变性,还来自树是由叶子构建的事实。这自然会阻止节点将其任何祖先作为子节点。如果你在另一个方向上构建树,你会引入循环的可能性

我的想法是对的,还是循环的不可能性仅仅来自不变性?考虑到来源,我想知道我是否遗漏了什么


编辑:在仔细考虑之后,似乎从叶子上建立起来可能是创建不可变树的唯一方法。我说得对吗

如果您使用的是一个不可变的数据结构,在严格的(而不是懒惰的)语言中,就不可能创建一个循环;由于必须按一定顺序创建元素,并且一旦创建了元素,就不能将其变异为指向稍后创建的元素。因此,如果您创建了节点n,然后创建了指向n的节点m(可能是间接的),您永远无法通过使n指向m来完成循环,因为不允许您突变n,也不允许n已经指向的任何对象


是的,你是对的,你只能通过从树叶上建立起来来创造一棵不变的树;如果从根开始,则必须在创建子对象时修改根对象,使其指向子对象。只有从叶子开始,创建每个节点以指向其子节点,才能从不可变的节点构建树。

不能从根节点构建树,它需要对已添加的节点进行变异

当你说“从叶子上构建”时,我猜你是在说构造器接受子元素,但从不接受父元素

看来如果你把这棵树建在 另一个方向,你会介绍 循环的可能性

不,因为这样会有相反的约束:构造函数必须接受父对象,但决不能接受子对象。因此,在所有祖先都被创建之前,您永远无法创建后代。因此,不可能有循环

再仔细考虑一下,它 好像是从树叶上爬起来的 这可能是创建 不变树。我说得对吗

不。。。见我对Brian和ergosys的评论

对于许多应用程序,其子节点指向其父节点的树不是很有用。我同意。如果需要按照树的层次结构确定的顺序遍历树,则向上指向的树会使遍历变得困难

然而,对于其他应用程序,这种类型的树正是我们想要的。例如,我们有一个文章数据库。每篇文章可以有一个或多个译本。每个翻译都可以有翻译。我们将此数据结构创建为关系数据库表,其中每个记录都有一个指向其父项的“外键”(指针)。这些记录都不需要更改指向其父记录的指针。添加新文章或译文时,将使用指向相应父级的指针创建记录

一个常见的用例是查询翻译表,查找特定文章的翻译或特定语言的翻译。啊,你说,翻译表是一个可变的数据结构

当然是。但它和树是分开的。我们使用(不可变)树来记录层次关系,使用可变表来对项目进行迭代。在非数据库情况下,可以有一个指向树节点的哈希表。无论哪种方式,树本身(即节点)都不会被修改

,包括如何有效地访问节点

我的观点是,OP问题的答案是“是的”,我同意你们其他人的观点,即周期的预防确实来自不变性本身。虽然你可以在另一个方向(自上而下)构建一棵树,但是如果你这样做了,并且它是不可变的,它仍然不能有循环

当你谈论强大的理论保证时,比如

不可变数据结构的另一个好特性是 不可能意外(或 创建一棵 包含一个循环[强调原文]

“这样一棵树不会很有用”相比之下就显得微不足道了——即使它是真的。 人们总是在无意中创建了无用的数据结构,更不用说故意创建所谓无用的数据结构了。假定的无用性不能保护程序免受数据结构中周期陷阱的影响。理论上的保证(假设你真的符合它规定的标准)


另外,指向上的树的一个很好的特性是,您可以保证指向下的树数据结构(如Eric Lippert)不具备的树定义的一个方面:每个节点最多有一个父节点。(请参阅和我的回答。)

如果你真的想努力尝试,你可以创建一个包含不可变循环的树。例如,您可以定义一个不可变的图形类,然后说:

Graph g = Graph.Empty
  .AddNode("A")
  .AddNode("B")
  .AddNode("C")
  .AddEdge("A", "B")
  .AddEdge("B", "C")
  .AddEdge("C", "A");
嘿,你有一棵“树”,里面有“圈”——因为你首先当然没有树,你有一个有向图


但是,如果数据类型实际上使用传统的二叉树的“左、右子树”实现,那么就无法生成循环树(当然,模运算是使用反射或不安全代码等狡猾的技巧)。

既然数据结构是不可变的,那么从另一个方向创建树不是不可能吗,所以