我目前正在教自己Haskell,我想知道在Haskell中使用字符串时最佳做法是什么。
Haskell中的默认字符串实现是Char列表。根据{{3}},这对于文件输入输出是低效的,因为每个字符都是单独分配的(我假设这意味着String基本上是Haskell中的链表,但我不确定。)
但是如果默认的字符串实现对于文件i / o来说效率低,那么在内存中使用字符串也是低效的吗?为什么或者为什么不? C使用char数组来表示String,我认为这将是大多数语言中默认的处理方式。
正如我所看到的,String的列表实现将占用更多内存,因为每个字符都需要开销,并且还有更多时间进行迭代,因为需要指针解除引用才能到达下一个char。但到目前为止我喜欢和Haskell一起玩,所以我想相信默认的实现是有效的。
答案 0 :(得分:33)
除了String / ByteString之外,现在有Text库,它结合了两者的优点 - 它与Unicode一起使用,同时在内部基于ByteString,因此您可以获得快速,正确的字符串。
答案 1 :(得分:30)
在Haskell中使用字符串的最佳实践基本上是:使用Data.ByteString / Data.ByteString.Lazy。
http://hackage.haskell.org/packages/archive/bytestring/latest/doc/html/
就Haskell中默认字符串实现的效率而言,它并非如此。每个Char
代表一个Unicode代码点,这意味着每Char
至少需要21位。
由于String
只是[Char]
,这是Char
的链接列表,这意味着String
的引用位置较差,这意味着{{} 1}} s在内存中相当大,至少它是String
,其中N是字符串的长度,M是指针的大小(32,64,你有什么),不像许多其他地方Haskell使用其他语言可能使用不同结构的列表(我在这里特别考虑控制流),N * (21bits + Mbits)
不太可能被编译器优化为循环等。
虽然String
对应于代码点,但Haskell 98报告并未指定执行文件IO时使用的编码,甚至没有指定默认值,更不用说更改它。在实践中,GHC提供了扩展,例如,二进制IO,但无论如何你都要离开预订。
即使在前置字符串之类的操作中,Char
也不太可能在实践中击败String
。
答案 2 :(得分:7)
答案比“使用延迟字节串”更复杂。
也就是说,如果你需要性能并且你可以纯粹用字节串表示你的数据,那么就这样做。
答案 3 :(得分:6)
给出的基本答案,使用ByteString,是正确的。也就是说,我之前的所有三个答案都有不准确之处。
关于UTF-8:这是否会成为一个问题完全取决于你对字符串的处理方式。如果您只是将它们视为单个数据块(包括连接操作,但不包括拆分),或者执行某些有限的基于字节的操作(例如,以字节为单位查找字符串的长度,而不是以字节为单位的长度)你不会有任何问题。如果您使用的是I18N,那么只有使用String
而非ByteString
的其他问题才会开始解决您遇到的极少数问题。
将单个字节预先添加到ByteString的前面可能比为String执行相同操作更昂贵。但是,如果您正在做很多这样的事情,那么可能会找到处理更便宜的特定问题的方法。
但最终的结果是,对于原始问题的海报:是的,字符串在Haskell中效率低下,虽然相当方便。如果您担心效率,请使用ByteStrings,并将它们视为Char8或Word8的数组,具体取决于您的目的(ASCII / ISO-8859-1与某种类型的Unicode,或只是任意二进制数据)。通常,使用Lazy ByteStrings(其中前置字符串的开头实际上是一个非常快速的操作),除非你知道为什么你想要非懒惰的(通常包含在对延迟评估的性能方面的评价)。 / p>
对于它的价值,我正在构建一个完全在Haskell中的自动交易系统,我们需要做的一件事就是快速解析我们通过网络连接收到的市场数据源。我可以使用可忽略不计的CPU来处理每秒300条消息的读取和解析;就处理这些数据而言,GHC编译的Haskell与C的距离足够接近,以至于它无法进入我的重要问题列表。