为什么没有使用FingerTree来实现稳定的实现?

时间:2012-06-22 07:16:30

标签: haskell data-structures finger-tree

前段时间,我遇到了an article on FingerTrees(另见an accompanying Stack Overflow Question)并提出了这个想法。我终于找到了使用它们的理由。

我的问题是Data.FingerTree package似乎在边缘有点腐烂。此外,容器包中的Data.Sequence使用数据结构re-implements一个(可能更好)版本,但不导出它。

理论上这个结构似乎很有用,但它似乎没有得到很多实际使用或关注。有人发现FingerTrees作为一个实际问题没有用处,或者这是一个不够关注的案例?


进一步说明

我有兴趣构建一个包含具有良好串联属性的文本的数据结构。考虑从各种片段构建HTML文档。大多数预构建的解决方案使用字节串,但我真的想要正确处理Unicode文本的东西。我目前的计划是将Data.Text片段分层为FingerTree。

我还想借用Data.Vector借用切片而无需使用(偏移,长度)操作进行复制。 Data.Text.Text内置于数据类型,但仅用于高效的uncons和unsnoc opperations。在FingerTree中,这些信息很容易成为树的v或注释。

3 个答案:

答案 0 :(得分:17)

为了回答关于手指树的问题,我认为问题在于它们与阵列相比具有相对较高的恒定成本,并且比实现有效连接的其他方式更复杂。 Builder只有一个更有效的界面来添加块,它们通常很容易获得(参见@ informatikr的答案中的链接)。假设Data.Text.Lazy是使用链接的列表列表实现的,并且您正在从构建器创建Data.Text.Lazy。除非你有很多块(可能超过50块),或者反复访问列表末尾附近的数据,否则手指树的高成本可能是不值得的。

Data.Sequence实现专门出于性能原因,并不像fingertree包提供的完整界面那样通用。这就是它不出口的原因;除Sequence之外的其他任何东西都不可能使用它。

我还怀疑许多程序员对如何实际使用monoidal注释感到茫然,因为它背后是一个相当重要的抽象障碍。很多人不会使用它,因为与其他数据类型相比,它们看不出它是如何有用的。

直到我在word numberspart2part3part4)上阅读Chung-chieh Shan的博客系列时,我才真正理解。这证明了这个想法绝对可以用在实际的代码中。

在您的情况下,如果您需要检查部分结果并具有有效的附加,则使用fingertree可能比构建器更好。根据构建器的实现,当您转换为Text时,最终可能会进行大量重复工作,向构建器添加更多内容,再次转换为Text等等。这取决于您的使用情况虽然模式。

您可能对我的splaytree包感兴趣,该包提供带有monoidal注释的splay树,并在它们上构建了几个不同的结构。除了展开树本身,SetRangeSet模块具有或多或少的完整API,Sequence模块主要是我用于测试的骨架。这不是一个“电池包含”解决方案,你正在寻找的东西(同样,@ informatikr的答案提供了那些),但如果你想试验monoidal注释它可能比Data.FingerTree更有用。请注意,如果您按顺序遍历所有元素(或连续snoc到末尾或类似),则splay树可能会失去平衡,但如果追加和查找是交错的,则性能可能非常好。

答案 1 :(得分:9)

除了John Lato的回答,我还会添加一些关于手指树性能的具体细节,因为我过去花了一些时间来研究它。

广泛的总结是:

  • Data.Sequence有很大的常数因子和渐近因子:当访问列表的前面(两个数据结构都有O(1)渐近)时,它几乎和[]一样快,并且列表中其他位置的速度要快得多(其中Data.Sequence是对数渐近线,[]的线性渐近线。

  • Data.FingerTreeData.Sequence具有相同的渐近线,但速度要慢一个数量级。

就像列表一样,指纹树具有很高的每元素内存开销,因此它们应该与分块结合使用以获得更好的内存和缓存使用。实际上,有几个包执行此操作(yitrifectarope)。如果Data.FingerTree可以在性能上接近Data.Sequence,我希望看到Data.Text.Sequence类型,它实现了Data.Text值的手指树。这种类型会丢失Data.Text.Lazy的流式传输行为,但会受益于改进的随机访问和连接性能。 (同样,我希望看到Data.ByteString.SequenceData.Vector.Sequence。)

现在实施这些措施的障碍是没有高效通用指纹树的实现(见下文我将进一步讨论)。为了生成Data.Text.Sequence的有效实现,必须完全重新实现专用于Text的手指树 - 就像Data.Text.Lazy完全重新实现列表一样,专门用于Text。不幸的是,手指树比列表复杂得多(尤其是concatenation!),所以这是相当多的工作。

所以我认为答案是:

  • 专业的手指树很棒,但很多工作要实施
  • chunked finger trees(例如Data.Text.Sequence很好,但目前Data.FingerTree的糟糕表现意味着它们不是共同的分块列表的可行替代品情况下
  • 构建器和分块列表实现了分块指树的许多好处,因此它们足以满足常见情况
  • uncommon 的情况下,建造者和分块列表不够,我们咬紧牙关,忍受分块指状树的不良常数因素(例如在yi和trifecta中)。

高效通用手指树的障碍

Data.SequenceData.FingerTree之间的大部分性能差距归因于Data.Sequence中的两个优化:

通过使用data families for generic unpacking并利用GHC的内联和专精,可以在Data.FingerTree的一般情况下应用这些优化 - 请参阅我的fingertree-unboxed package,这会带来一般性手指树的表现几乎达到Data.Sequence的表现。不幸的是,这些技术存在一些重大问题:

  • data families for generic unpacking is unpleasant for the user,因为他们必须定义大量实例。这个问题没有明确的解决方案。

  • 指树使用多态递归,GHC的专业人员不能很好地处理(12)。这意味着,为了在度量类型上获得足够的专业化,我们需要大量的INLINE pragma,这会导致GHC生成大量代码。

由于这些问题,我从未在Hackage上发布过该软件包。

答案 2 :(得分:7)

忽略您的手指树问题并仅回复您的进一步说明:您是否考虑过Data.Text.Lazy.Builder或专门用于构建HTML,blaze-html

两者都允许快速连接。对于切片,如果这对于解决问题很重要,那么它们可能没有理想的性能。