【发布时间】:2019-02-06 08:25:49
【问题描述】:
在阅读Real World Haskell 的第 13 章时,我想到了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
我知道这种方法会很急切,所以我们保留左操作数 small,而不是像作者所说的那样保留 yet to be applied methods 的 thunk,但是......我们仍然对每个追加执行遍历.
给定一个列表:[1...100] 并想追加 1,2 我仍然会遍历它 2 次,因为我会:
f(f([1..100]))= f([1..100,1])- 遍历 1 次并附加1[1..100,1,2]- 第二次遍历追加2
有人能告诉我这在时间复杂度上如何更有效吗? (因为space-complexity 是由于没有更多的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],那么在这种情况下difference list对于追加一个元素的单一操作如何更有效?
【问题讨论】:
-
一般见canonical answer。
-
只有
(x :)和(xs ++)允许在差异列表中。 (请注意,参数在左侧。)差异列表是有效的,因为它们强制每个操作进入前置。 -
那么
difference list和foldl '有什么区别?foldl '不是在每次迭代时都将新值附加到buffer,从而使我们免于thunks吗? -
请参阅 stackoverflow.com/questions/14938584/… 关于 foldl (免责声明:答案由我提供)。
foldl' (++)构建了((([1]++[2])++[3])++[4])++[5]的左倾链。 -
回答您的新问题,
(f 4 . f 5) $ a = ((: 4) . (: 5)) [1,2,3] = 4:5:[1,2,3]。如果您的意思是((++ [4]) . (++ [5])) [1,2,3] = (++ [4]) ([1,2,3]++[5]) = ([1,2,3]++[5])++[4],那么它再次构建了一个左倾链,即不好(二次)。 IOW,到底是什么不清楚?
标签: haskell optimization difference-lists