更新:这个问题是我在2012年6月8日发布的博客的主题。感谢您提出的好问题!
好问题。我们对您提出的问题进行了很长时间的辩论。
我们希望有一个具有以下特征的数据结构:
- 一成不变。
- 一棵树的形式。
- 从子节点便宜地访问父节点。
- 可以从树中的节点映射到文本中的字符偏移量。
- 坚持不懈。
通过持续我的意思是能力再使用大多数在树中的现有节点时,编辑是对文本的缓冲区的。由于节点是不可变的,因此重用它们没有障碍。我们需要这样做来提高性能;每次您按下键时,我们都无法重新解析文件中的大量杂物。我们只需要重新编制和重新分析仅受编辑影响的树的部分。
现在,当您尝试将所有这五种内容放到一个数据结构中时,立即会遇到问题:
- 首先如何构建节点?父母和孩子都互相指称,并且是不可变的,那么哪个首先被构建?
- 假设您设法解决了这个问题:如何使其持久化?您不能在其他父节点中重复使用子节点,因为这将告诉子节点其具有新的父节点。但是孩子是一成不变的。
- 假设您设法解决了这个问题:当在编辑缓冲区中插入新字符时,映射到该点之后某个位置的每个节点的绝对位置都会发生变化。这使得很难建立持久的数据结构,因为任何编辑都可以更改大多数节点的跨度!
但是在罗斯林团队中,我们通常会做不可能的事情。实际上,我们通过保留两棵解析树来完成不可能的事情。“绿色”树是不可变的,持久的,没有父级引用,是“自下而上”构建的,每个节点都跟踪其宽度,但不跟踪其绝对位置。进行编辑时,我们仅重建受该编辑影响的绿树部分,通常大约是树中所有解析节点的O(log n)。
“红色”树是围绕绿色树构建的不可变外观。它是按需 “自上而下”构建的,每次编辑都被丢弃。当您从顶部向下穿过树时,它通过按需制造父引用来计算它们。当您下降时,它通过从宽度计算绝对位置来制造绝对位置。
用户(您)只能看到红色的树;绿树是一个实现细节。如果您查看解析节点的内部状态,您实际上会发现其中存在对另一个解析节点的不同类型的引用。那是绿树节点。
顺便提一下,这些被称为“红色/绿色树”,因为它们是我们在设计会议中用来绘制数据结构的白板笔颜色。颜色没有其他含义。
这种策略的好处是我们获得了所有这些伟大的东西:不变性,持久性,父引用等等。代价是该系统很复杂,如果“红色”外观变大,则会消耗大量内存。目前,我们正在做实验,看看是否可以减少一些成本而不损失收益。