Haskell中有没有办法在列表末尾添加元素而不必遍历整个列表?
示例如果我有列表[1,2,3,4]
并希望在结尾添加5,例如:[1,2,3,4,5]
据我所知,++运算符必须解析整个列表,这是非常低效的。
谢谢!
答案 0 :(得分:4)
正如@Jubobs在评论中已经说过的那样 - 不,你不能,但这并不一定(总是)低效。
在开始做一些解释之前,我想提醒你唐纳德克努特和他的说法,即所有邪恶的根源都是过早的优化。因此,尝试编写正确的程序并在以后担心性能 - 当您遇到瓶颈时(并确保haskell具有良好的工具来检测瓶颈!)。
假设您有一个无限列表[1..]
并且您想要附加5
,您仍然可以在haskell中执行此操作。让我们开启ghci并观察一些事情。
> ghci
GHCi, version 8.0.2: http://www.haskell.org/ghc/ :? for help
Prelude> let a = [1..] ++[5] :: [Int]
首先我们注意到 - let
- 绑定工作懒惰而且没有打印(尚未)。我们甚至可以进一步检查a
的评估范围。
Prelude> :sprint a
a = _
虽然filter (== 5) a
之类的表达式永远不会终止,但我们仍然可以使用列表a
执行有用的操作。
Prelude> let b = map (+2) a
Prelude> take 10 b
[3,4,5,6,7,8,9,10,11,12]
再次检查a
。
Prelude> :sprint a
a = 1 : 2 : 3 : 4 : 5 : 6 : 7 : 8 : 9 : 10 : _
我们只评估了前十个元素。
总结这个例子,我们看到 - 即使你“追加”5
它也许永远不会被使用 - 因此“附加”将永远不会发生,这比我们有一个便宜的“追加”更有效率那件事发生了。
(注意,这种懒惰会引入一些可能导致麻烦的内存开销,请参阅Lazy Evaluation - Space Leak以获取更多信息。)
正如其他人已经说过的那样 - 可以选择使用好的旧列表。
O(1)
次操作均为(:)
,反之为O(n)
- 与O(n)
步骤的(++)
相比。使用不同的数据结构:
等同于
[1,2,3]
的列表的差异列表将是函数\xs -> [1,2,3] ++ xs
。普通的空列表是[]
,而空的差异列表是函数\xs -> [] ++ xs
。
Sequence
支持高效的prepend
/ append
- 操作并查找边缘元素。< / p>
$ stack ghci --package containers
Prelude> import Data.Sequence
Prelude Data.Sequence> let a = fromList [1..4] :: Seq Int
Prelude Data.Sequence> (a |> 5)
fromList [1,2,3,4,5]
Prelude Data.Sequence> 0 <| (a |> 5)
fromList [0,1,2,3,4,5]
Set
- 不像列表那样排序,但仍然支持高效(O(log n)
)插入,但只允许进行唯一插入。
$ stack ghci --package containers
Prelude> import Data.Set
Prelude Data.Set> let a = fromList [1..4] :: Set Int
Prelude Data.Set> insert 5 a
fromList [1,2,3,4,5]
Prelude Data.Set> insert 4 a
fromList [1,2,3,4]