【问题标题】:Why list ++ requires to scan all elements of list on its left?为什么 list ++ 需要扫描其左侧列表的所有元素?
【发布时间】:2016-02-18 07:51:21
【问题描述】:

Haskell 教程说,当我们使用“Hello”++“World”时,新的列表构造必须访问所有单个元素(这里是“Hello”的每个字符),所以如果列表在“++”左边很长,那么使用“++”会降低性能。

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

有什么提示吗?谢谢。

【问题讨论】:

    标签: performance list haskell concat


    【解决方案1】:

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

    【讨论】:

    • 你的意思是 Data.Sequence 是一个可变类型,所以它维护一个指向它的尾部的指针/指示符?
    • @HindForsum 从技术上讲,Data.Sequence 是一棵,因此对于大多数操作来说它具有对数复杂度。但它是一个手指树,所以连接速度特别快。没有什么是可变的。
    • @HindForsum,没有。它是不可变的,由 2-3 指树表示。这提供了与到最近端的距离的对数成比例的时间访问、修改和拆分,在较短序列的长度上按时间对数连接,以及许多其他令人愉快的事情。去看看,阅读论文,玩它,等等。为我自己的微薄贡献获取一个合理的最新版本。
    【解决方案2】:

    如果你有一个不可变的列表,它有一个头和一个对尾的引用,你不能改变它的尾。如果你想在列表的“末尾”添加一些东西,你必须到达末尾,然后将所有项目一个接一个地放在右侧列表的开头。这是不可变列表的基本属性:连接是昂贵的。

    【讨论】:

      【解决方案3】:

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

      【讨论】:

      • 它们由一个头部组成[...]空列表呢?
      • 好吧,空列表没有头(duh)。我现在明确提到了。
      • 我只是在吹毛求疵:)
      • 在连接时,“左列表”的副本已完成并且已经遍历到它的末尾,然后连接“右列表”。没有多余的遍历,怎么会慢?
      • @HindForsum 除了复制左侧列表时需要做的遍历之外,没有额外的遍历。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-10-22
      • 2015-01-25
      • 2021-11-12
      • 2020-06-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多