Finger Tree (Data.Sequence)和绳索(Data.Rope)(Edward Kmett's version或Pierre-Etienne Meunier's version之间的主要区别是什么?
在Haskell库中,Data.Sequence具有更多功能。我认为绳索可以更有效地处理“阻塞”。
作为程序员考虑效率处理,比如一个700万字符的序列,我需要做的事情(a)插入任何地方,(b)剪切和粘贴段(拼接),(c)搜索和替换子字符串哪个更有效率?
对ehird的回应澄清:
我的算法的大部分都运行了数千个搜索替换操作,如s/(ome)?reg[3]x/blah$1/g
,以反复改变数据。所以我需要有效的正则表达式模式匹配(可能是regex-tdfa?)以及拼接(data [a:b] = newData),其中不是必然{ {1}}
Lazy ByteStrings可能没问题,但是拼接呢?拼接ByteString是O(dataSize / chunkSize)线性时间(用于搜索),加上(可能是?)开销用于维护恒定大小的块。 (后一部分可能是错的); vsT(Log(dataSize))用于FingerTree。
我的“容器”数据类型抽象地是有限字母。它可以具体表示(length(newData) == b-a+1)
s或Char
s或Byte
s甚至是假设的Word8
s(半字节)。
**我有一个关于如何有效使用Word4
或newtype
的相关问题,以便我的代码可以引用抽象字母表,但编译后的程序仍然可以高效。 (我应该单独发布这个问题。)
性能问题:也许Seq远比ByteString差(通过q显着的常数因子)。在简单的测试中,将7MB读入严格的data
,然后将其打印到60MB真实内存使用的控制台峰值(根据Windows Process Manager),但将该内容加载到ByteString
然后打印使用400MB ! (我应该单独发布这个问题,包括代码和分析详细信息。)
平台问题:我正在使用EclipseFP和Haskell平台。我在我的机器上安装了Text,我想尝试一下,但我的Eclipse环境找不到它。每当我使用Seq Char
(安装了不兼容的软件包版本,cabal install
vs --user
混淆)时,我就会遇到严重问题,所以我想坚持使用EclipseFP可以找到的平台软件包。我认为Text将进入下一版本的平台,所以这将很好。
Trifecta :我简短地看到了Trifecta,这加剧了我的困惑。 (为什么它有自己的已经发布的一般数据结构的新实现?它们更好吗?太多几乎相同的选项!)
已编辑,其中包含更多详细信息和改进的链接。
@ ehird的总结是主要的回归点。绳索,或ByteStrings或矢量的手指树加上一个小的自定义幺半群。无论哪种方式,我都必须编写一个简单的正则表达式实现来粘合。
鉴于所有这些信息,我建议使用绳索或建筑物 你自己的结构与它所基于的fingertree包(相反 比Seq,所以你可以适当地实现像长度这样的东西 用叶子测量的类型 - 见幺半群和手指树 数据分块为未装箱的Vector。当然,后者更多 工作,但让你专门针对你的用例进行优化。无论哪种方式, 绝对将它包装在一个抽象的界面中。
我今天晚些时候会回来并分成新的问题。我将整理出低级技术问题,然后再回到整体比较中。 我将更改问题标题以更好地反映我真正关心的问题“哪些Haskell模块提供或支持我需要的序列操作操作?”谢谢你去ehird和其他响应者。
答案 0 :(得分:16)
对于本答复的其余部分,我假设您实际上是在尝试存储原始字节,而不是字符。如果要存储字符,则应考虑使用text(对于Unicode文本等效ByteString
)或基于它编写自己的基于fingertree的结构。您还可以将ByteString
与Data.ByteString.UTF8包中的utf8-string模块一起使用;我认为最终可能会提高效率,但对于Unicode文本而言,它的功能要比Text
更全面。
嗯,你链接的绳子包只存储了ByteString
s的等价物,而Seq
是通用的,可以处理任何类型的数据;前者可能更有效地存储字符串。
我怀疑它是相同的基本树结构,因为绳索实现"字节串的指尖"和Seq
是2-3指树;它取决于(并因此可能使用)fingertree包,它与Data.Sequence基本相同,但更通用。绳索可能会将数据打包成短ByteString
秒,这对Seq
是不可能的(不会破坏length
等操作。)
总而言之,如果您要存储字节串数据,绳索看起来就像是一个更好的结构,并且它似乎具有花哨的功能,用于注释"字符串的一部分;但是,它最后一次更新是在7月份,同一位作者(8月首次发布)的新trifecta解析器组合库包含其own set of rope modules,因此在其上建立新代码可能是不明智的。当然,对三连胜的改变可能与一般用途无关,并且将这些改变作为新版本的绳索分开可能并不困难;也许他们没有成功的唯一原因是因为三连已经有很多依赖:)
但是,如果您在处理过程中的任何一点都需要通用容器类型(例如,将字节解析为更丰富的表示序列),或者想要坚持Haskell平台中的内容,那么您&# 39;我需要使用Seq
。
您确定ByteString
或Text
(因为您提到的字符)不适合您正在做的事情吗?它们存储偏移量和长度字段,因此获取子字符串不会导致任何复制。如果你的插入操作很少,那么它可能会成功。基于IntMap
的某种结构也可能值得考虑。
回答您的最新问题:
ByteString
s:请注意,默认情况下,懒惰ByteString
使用64 KiB块,您可以使用{{{{{{ 1}}手动。但你是对的,手指树可能更适合;只是做了更多的工作,已经为你做了懒惰的fromChunks
s。ByteString
)表示这个字母表序列的类型。这样,你可以尝试各种实现,同时希望本地化必须完成的工作,这样你就可以根据真实的性能数据而不是猜测来选择:)当然,编写一个新的仍然是一个前期成本实现。就您的其他问题而言,newtype
在编译时被删除,因此newtype
具有与其包装类型相同的运行时表示。简而言之:不要担心在newtype
中包装内容。newtype
完全是懒惰和盒装,并且不会被" chunking" Seq Char
就像Char
一样{;}}它可能比Rope
的内存效率更低。像String
之类的东西可能会更好地执行 lot ,但除非你的块大小不变,否则你将无法获得有意义的长度等等而不会遍历整个事物。 至于#3,而不是Seq ByteString
,您可能需要考虑unboxed Vector
;这样,您就可以使用抽象字母表类型,而不是将内容隐藏到ByteString
基于ByteString
的界面中。
鉴于所有这些信息,我建议使用Word8
,或使用fingertree基于({1}}的{{3}}软件包构建您自己的结构,以便您可以使用Rope
类型类正确实现Seq
之类的内容 - 请参阅Monoids and Finger Trees),并将叶数据分块为未装箱的length
。当然,后者是更多的工作,但让你专门针对你的用例进行优化。无论哪种方式,绝对将它包装在一个抽象的界面中。
顺便说一句,正则表达式在Haskell生态系统中并不像它们那样得到良好支持;如果有任何选择,可能值得考虑使用其他东西。但是,它过分依赖于您的计划的具体细节来提供具体的建议。