当我阅读第 13 章时,Real World Haskell
我想到了Difference Lists
.
作者说,在命令式语言中,如果我们想将一个元素附加到一个列表中,代价就是O(1)
我们会保留一个指向最后一个元素的指针。但是在 Haskell 中,我们有immutable
对象,所以我们每次都需要遍历列表并将元素附加到末尾,因此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
的那样保留左操作数,而不是保留左操作数small
,但是....我们仍然对每个追加执行遍历。
给定一个列表:[1...100]
并且想要追加1,2
我仍然会遍历它2
,因为我会:
f(f([1..100]))= f([1..100,1])
- 遍历 1 次并附加1
[1..100,1,2]
- 遍历第二次追加2
有人能告诉我这在时间复杂度上如何更有效吗?(因为space
-complexity 不再是因为thunks
, like foldl'
)
附言
我知道规范的答案,并且我也阅读了这一章,我觉得这很有帮助。
我知道您可以O(1)
通过使用 追加到左侧来实现复杂性:
,但它不会类似于++
.
如果我使用f=(:)
on a= [1,2,3]
:
(f 4 . f 5) $ a
我可以说我0(1)
对每个追加都有效率,因为我总是在左侧追加,但我不会得到[1,2,3,4,5]
,我会得到[5,4,1,2,3]
,那么在这种情况下difference list
,对于追加的单一操作如何更有效一个元素?