为什么“ ++”功能比“:”昂贵得多?

时间:2018-09-13 13:35:22

标签: haskell

以下段落来自Learn You a Haskell for Great Good!

  

“当在长字符串上反复使用++运算符时要当心。将两个列表放在一起时(即使将单例列表附加到列表中,例如:[1,2,3] ++ [4] ),在内部,Haskell必须遍历++左侧的整个列表。在处理不太大的列表时这不是问题,但是在长度为五千万个条目的列表末尾放置内容是但是,使用:运算符(也称为cons运算符)将某些内容放在列表的开头是即时的。“

我不知道为什么Haskell必须遍历++左侧的整个列表。

2 个答案:

答案 0 :(得分:4)

rhs(右侧)上的列表必须在lems的最后一个元素之后 。由于haskell列表是根据后继者实现的,因此您需要“到达”最后一个元素以向其添加任何内容,即,使要添加的列表成为后继者。

如果仅存储对第一个元素的引用,这类似于以命令式语言连接单链列表。您只能追加到最后一个,但是要找到它,您需要遍历所有链接。

如果实现自己的列表,则由于语法更改而变得更加明显:

data List a = Empty | Cons a (List a)

Cons 1 (Cons 2 (Cons 3 Empty)))

要追加到这样的列表,您需要更改中间Empty * 。但是,从外部(例如通过模式匹配)查看值,您只会看到Cons 1 <tail>。在您进行评估并看到tail等之前,Cons 2 <tail>部分有些模糊,这是您试图避免的。

相反,前置将只是Cons 0 <the list above>,包装整个列表而无需查看它。这就是为什么您可以写0 : [1..]之类的东西而不能写[1..] ++ [42]之类的东西的原因。


* 创建一个与该特定点不同的新列表。 Haskell列表(通常是值)显然是不可变的。

答案 1 :(得分:1)

Haskell中类似[3,1,5,6]的列表如下:

3 : (1 : (5 : [6])),其中:是cons函数。显然,您仍然可以在Haskell中编写类似[3,1,5,6]的列表,但这只是花哨的语法。

在6后面添加元素非常困难,如您所见:6一直嵌套在列表的下方。为了添加项目,Haskell需要完全解构列表,然后才能添加项目,如下所示:

(++) (x : xs) otherList = x : (xs ++ otherList)
(++) [x] otherList = x : otherList

这很可能不是(++)的实际定义,但这显示了问题。 (++)运算符必须遍历整个最左边的列表,以便将列表中的最后一项限制为新列表,然后将所有其他项递归合并。