gpt4 book ai didi

haskell - 了解差异列表的概念

转载 作者:行者123 更新时间:2023-12-03 16:12:49 24 4
gpt4 key购买 nike

当我阅读 Real World Haskell 的第 13 章时,我想到了差异列表的概念。
作者说,在命令式语言中,如果我们想将一个元素附加到列表中,成本将为 O(1),因为我们会保留指向最后一个元素的指针。然而在 Haskell 中,我们有 不可变 对象,所以我们每次都需要遍历列表并在末尾附加元素,因此 0(n)

代替 [1,2,3]++[4]++[5]
我们可以使用部分应用:(++4).(++5) [1,2,3].

我不明白这怎么更有效率,因为:
- 当我执行 (++[5]) [1,2,3] 时,它仍然是 O(n) 然后是另一个 0(n) (++4)

报价

There are two very interesting things about this approach. The first is that the cost of a partial application is constant, so the cost of many partial applications is linear. The second is that when we finally provide a [] value to
unlock the final list from its chain of partial applications, application
proceeds from right to left. This keeps the left operand of (++) small, and so
the overall cost of all of these appends is linear, not quadratic


我知道这种方法会很急切,所以我们没有保留 yet to be applied methods 的 thunk,而是保留了左操作数 small 作为作者说,但是......我们仍然对每个追加执行遍历。

给定一个列表:[1...100] 并且想要附加 1,2 我仍然会遍历它 2 次因为我会:

  1. f(f([1..100]))= f([1..100,1]) - 遍历 1 次并附加 1

  2. [1..100,1,2] - 第二次遍历追加2

有人能告诉我这在时间复杂度上如何更有效率吗? (因为空间-复杂性是由于没有更多的thunks,比如foldl')


附言

我知道规范的答案,我也阅读了 this chapter我发现这非常有帮助。
我知道您可以通过使用 : 附加到左侧来实现 O(1) 复杂性,但它不会相似到 ++

如果我在 a= [1,2,3] 上使用 f=(:) :
(f 4 . f 5) $ a
我可以说每次追加的效率都是0(1),因为我总是在左边追加,但我不会得到 [1,2,3,4,5] ,我会得到 [5,4,1,2,3] ,所以怎么样在这种情况下,差异列表对于附加一个元素的单一操作更有效?

最佳答案

你需要更加注意时间,即什么时候这件事或那件事正在发生。

我们不是从列表[1,2,3]开始,而是从不同的列表开始

f1 = ([1,2,3] ++)

然后将 4、5“添加”到不断增长的差异列表的末尾,我们有

f2 = f1 . ([4] ++)
f3 = f2 . ([5] ++)

在不断增长的差异列表中末尾的每次添加都是O(1)

当我们最终完成构建时,我们通过应用程序将其转换为“普通”列表

xs = f3 []    -- or f3 [6..10] or whatever

然后,仔细地,我们得到

xs = ((([1,2,3] ++) . ([4] ++)) . ([5] ++)) []
= (([1,2,3] ++) . ([4] ++)) ( ([5] ++) [] )
= ([1,2,3] ++) ( ([4] ++) ( ([5] ++) [] ))
= 1:2:3: ( 4 : ( 5 : [] ))

根据 (++) 的定义。

一个规范的答案:Why are difference lists more efficient than regular concatenation?


即使 a1 = (++ [4]) [1..] 本身 也是一个 O(1) 操作,因为是 a2 = (++ [5]) a1a3 = (++ [6]) a2,因为 Haskell 是惰性的,thunk 创建是 O (1).

只有当我们访问最终结果时,整个操作才变成二次的,因为访问 ++ 结构并没有重新排列它——它仍然是左嵌套的,所以 quadratic 从顶部重复访问。

通过向 [] 应用左嵌套 . 结构到普通列表的转换会在内部将该结构重新排列到右嵌套的 $结构,如规范答案中所述,因此从顶部重复访问此类结构是线性的。

所以区别在于 ((++ [5]) . (++ [4])) [1,2,3] (坏)和 ((([ 1,2,3]++) . ([4]++)) . ([5]++)) [](好)。构建函数链 ((++ [4]) . (++ [5])) 本身是线性的,是的,但它创建了一个二次结构来完全访问。

但是 ((([1,2,3]++) . ([5]++)) . ([4]++)) [] 变成了 ([ 1,2,3]++) (([5]++) (([4]++) []))

关于haskell - 了解差异列表的概念,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54549283/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com