Performance 为什么要列出++;需要扫描列表左侧的所有元素吗?

Performance 为什么要列出++;需要扫描列表左侧的所有元素吗?,performance,list,haskell,concat,Performance,List,Haskell,Concat,Haskell教程说,当我们使用“Hello”+“World”时,要小心,新的列表结构必须访问所有单个元素(这里是“Hello”的每个字符),因此如果“++”左侧的列表很长,那么使用“+”将降低性能 我想我没有正确理解,Haskell的开发人员从来没有调整过列表操作的性能吗?为什么这个操作仍然很慢,在任何lambda函数或curry中都有某种语法一致性 有什么提示吗?谢谢。如果您有一个头部和尾部引用的不可变列表,则不能更改其尾部。如果你想在列表的“末尾”添加一些内容,你必须到达末尾,然后将所有项

Haskell教程说,当我们使用“Hello”+“World”时,要小心,新的列表结构必须访问所有单个元素(这里是“Hello”的每个字符),因此如果“++”左侧的列表很长,那么使用“+”将降低性能

我想我没有正确理解,Haskell的开发人员从来没有调整过列表操作的性能吗?为什么这个操作仍然很慢,在任何lambda函数或curry中都有某种语法一致性


有什么提示吗?谢谢。

如果您有一个头部和尾部引用的不可变列表,则不能更改其尾部。如果你想在列表的“末尾”添加一些内容,你必须到达末尾,然后将所有项目逐一放在右边列表的开头。这是不可变列表的基本属性:连接是昂贵的。

Haskell列表类似于单链接列表:它们要么是空的,要么由一个头部和一个(可能是空的)尾部组成。因此,在向列表中添加某些内容时,首先必须遍历整个列表才能到达末尾。因此,您最终将遍历整个列表(即您附加到的列表),这需要O(n)运行时。

在某些语言中,“列表”是一种通用的序列类型,旨在为串联、拆分等提供良好的性能。在Haskell和大多数传统函数式语言中,列表是一种非常特定的数据结构,即单链表。如果您想要一个通用的序列类型,您应该使用
容器
包中的
Data.sequence
(该包已经安装在您的系统上,并且为各种操作提供了非常好的大O渐近性),或者可能是其他一些针对常见使用模式进行了大量优化的序列。

它们由一个头部组成[…]空列表呢?好吧,空列表没有头(duh)。我现在明确提到了这一点。我只是在串联时吹毛求疵
:)
,一个“左列表”的副本已经完成并遍历到它的末尾,然后连接到“右列表”。没有额外的遍历,怎么会慢?@HindForsum除了复制左侧列表时需要执行的遍历之外,没有额外的遍历。你是说Data.Sequence是一种可变类型,因此它保留了指向其尾部的指针/指示符吗?@HindForsum.Sequence是一个树,因此对于大多数操作,它具有对数复杂性操作。但它是一个手指树,所以连接速度特别快。它没有任何可变的地方。@HindForsum,不。它是不可变的,由2-3个手指树表示。这提供了访问、修改和时间分割,时间与到近端距离的对数成正比,时间上的连接与距离的长度成对数看一看,读一读报纸,玩玩它,等等。为了我自己的微薄贡献,得到一个合理的最新版本。