前段时间,我遇到了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
或注释。
答案 0 :(得分:17)
为了回答关于手指树的问题,我认为问题在于它们与阵列相比具有相对较高的恒定成本,并且比实现有效连接的其他方式更复杂。 Builder只有一个更有效的界面来添加块,它们通常很容易获得(参见@ informatikr的答案中的链接)。假设Data.Text.Lazy
是使用链接的列表列表实现的,并且您正在从构建器创建Data.Text.Lazy
。除非你有很多块(可能超过50块),或者反复访问列表末尾附近的数据,否则手指树的高成本可能是不值得的。
Data.Sequence
实现专门出于性能原因,并不像fingertree
包提供的完整界面那样通用。这就是它不出口的原因;除Sequence
之外的其他任何东西都不可能使用它。
我还怀疑许多程序员对如何实际使用monoidal注释感到茫然,因为它背后是一个相当重要的抽象障碍。很多人不会使用它,因为与其他数据类型相比,它们看不出它是如何有用的。
直到我在word numbers(part2,part3,part4)上阅读Chung-chieh Shan的博客系列时,我才真正理解。这证明了这个想法绝对可以用在实际的代码中。
在您的情况下,如果您需要检查部分结果并具有有效的附加,则使用fingertree可能比构建器更好。根据构建器的实现,当您转换为Text
时,最终可能会进行大量重复工作,向构建器添加更多内容,再次转换为Text
等等。这取决于您的使用情况虽然模式。
您可能对我的splaytree包感兴趣,该包提供带有monoidal注释的splay树,并在它们上构建了几个不同的结构。除了展开树本身,Set
和RangeSet
模块具有或多或少的完整API,Sequence
模块主要是我用于测试的骨架。这不是一个“电池包含”解决方案,你正在寻找的东西(同样,@ informatikr的答案提供了那些),但如果你想试验monoidal注释它可能比Data.FingerTree
更有用。请注意,如果您按顺序遍历所有元素(或连续snoc到末尾或类似),则splay树可能会失去平衡,但如果追加和查找是交错的,则性能可能非常好。
答案 1 :(得分:9)
除了John Lato的回答,我还会添加一些关于手指树性能的具体细节,因为我过去花了一些时间来研究它。
广泛的总结是:
Data.Sequence
有很大的常数因子和渐近因子:当访问列表的前面(两个数据结构都有O(1)渐近)时,它几乎和[]
一样快,并且列表中其他位置的速度要快得多(其中Data.Sequence
是对数渐近线,[]
的线性渐近线。
Data.FingerTree
与Data.Sequence
具有相同的渐近线,但速度要慢一个数量级。
就像列表一样,指纹树具有很高的每元素内存开销,因此它们应该与分块结合使用以获得更好的内存和缓存使用。实际上,有几个包执行此操作(yi,trifecta,rope)。如果Data.FingerTree
可以在性能上接近Data.Sequence
,我希望看到Data.Text.Sequence
类型,它实现了Data.Text
值的手指树。这种类型会丢失Data.Text.Lazy
的流式传输行为,但会受益于改进的随机访问和连接性能。 (同样,我希望看到Data.ByteString.Sequence
和Data.Vector.Sequence
。)
现在实施这些措施的障碍是没有高效和通用指纹树的实现(见下文我将进一步讨论)。为了生成Data.Text.Sequence
的有效实现,必须完全重新实现专用于Text
的手指树 - 就像Data.Text.Lazy
完全重新实现列表一样,专门用于Text
。不幸的是,手指树比列表复杂得多(尤其是concatenation!),所以这是相当多的工作。
所以我认为答案是:
Data.Text.Sequence
)将很好,但目前Data.FingerTree
的糟糕表现意味着它们不是共同的分块列表的可行替代品情况下 Data.Sequence
和Data.FingerTree
之间的大部分性能差距归因于Data.Sequence
中的两个优化:
度量类型专用于Int
,因此度量操作将编译为有效的整数运算,而不是
The measure type is unpacked into the Deep
constructor,它将指针解引用保存在树操作的内部循环中。
通过使用data families for generic unpacking并利用GHC的内联和专精,可以在Data.FingerTree
的一般情况下应用这些优化 - 请参阅我的fingertree-unboxed package,这会带来一般性手指树的表现几乎达到Data.Sequence
的表现。不幸的是,这些技术存在一些重大问题:
data families for generic unpacking is unpleasant for the user,因为他们必须定义大量实例。这个问题没有明确的解决方案。
指树使用多态递归,GHC的专业人员不能很好地处理(1,2)。这意味着,为了在度量类型上获得足够的专业化,我们需要大量的INLINE
pragma,这会导致GHC生成大量代码。
由于这些问题,我从未在Hackage上发布过该软件包。
答案 2 :(得分:7)
忽略您的手指树问题并仅回复您的进一步说明:您是否考虑过Data.Text.Lazy.Builder或专门用于构建HTML,blaze-html?
两者都允许快速连接。对于切片,如果这对于解决问题很重要,那么它们可能没有理想的性能。