我接受过lisp语言系列的培训,现在我已经接受过培训了 为了自己的利益,学习了一些Haskell。 在lisp中,功能样式是可以的,但有一些情况 必须要有必要的风格才能获得不错的表现,例如
附加
追加很慢,因为它必须复制它的第一个参数(有时是x100 和成功摆脱它的版本一样慢)。一个补救措施是 将第一个列表的最后一个指针移动到开头 第二个列表,而不是附加。当然这是一种破坏性的行动。
排序
quicksort的功能版本创建了许多中间列表, 它以某种方式违背了算法的目的,其中 要尽可能快。 AFAIR,在常见的lisp中,sort是唯一没有功能版本的破坏性功能。
长度
这在lisp中是一项代价高昂的操作,因为必须要做到这一点 整个列表来获取它的长度。这不一定是这样,afaik clojure 以对数时间计算列表长度。解决方案通常是 在命令性循环中动态计算长度。
我的问题是,我们如何在哈斯克尔处理这些问题?
答案 0 :(得分:11)
正如delnan所说,这些问题是使用错误的数据结构,例如当你想要一个向量时的链表。
您的特定操作
追加:cons是O(1),所以我怀疑你指的是分配cons单元格,单元格上的模式匹配以及单元格的最终GC的行为。除非您优化列表,否则这是不可避免的,这在某些情况下会发生。
排序:我建议您查看ST monad,它允许您在纯上下文中使用需要变异的算法。有关示例,请参阅vsort实现。
length:当然,获取链表长度的唯一方法是遍历列表。如果您经常需要长度,那么请考虑使用Set,Map或Vector - 所有这些都记录了可以在O(1)中访问的总大小。
一般
ST
的可变性。答案 1 :(得分:3)
append
不是普遍的事情。附加到双端队列不同于附加到缺点列表。破坏性附加不同于附加副本。根据您的标准,您可以针对缓慢或线程安全性或简单性进行优化,并且“问题”的定义将发生变化。
正如delnan和Thomas DuBuisson所提到的,如果您选择正确的数据类型,Haskell将拥有所有这些选项。
因此,如果您想要优化特定算法,可能会有一种方法将其转换为快速非破坏性版本,通常可以通过乘法log(n)
减速来模拟破坏的选项,或者选择在加性常数减速时运行引用透明的破坏。
有关此转换的问题的好例子,请查看持久联合查找算法或功能图库。