假设我们有一个列表
x = [1..10]
我们打算用这种方式创建另一个列表y:
y= [a|a<-x]
因此,在从y
创建列表x
时,它会访问x
的每个元素(从1到10)并以相同的顺序将其插入y
。由于haskell中的列表是单链表,我们只能在其头部插入一个新元素。首先,它将1插入[]
&amp;我们有[1]
。然后它将2插入其头部和底部。所以我们有[2,1]
。然后插入3&amp;我们有[3,2,1]
&amp;等等。所以最终我们应该y
为[10,9..1]
。但我们将y
作为[1..10]
。为什么会这样?
答案 0 :(得分:6)
因为它&#34;插入&#34;它们在列表的尾部(正在构建列表时),不是头部(参见&#34;尾部递归模数缺点&#34;):
[a | a <- [1..10]] ==
[a | a <- (1:[2..10])] == -- [a | a <- ([1] ++ [2..10])]
1 : [a | a <- [2..10]] -- [1] ++ [a | a <- [2..10]]
这是因为
[a | a <- (xs ++ ys)] == [a | a <- xs] ++ [a | a <- ys]
和
[a | a <- ys] == ys
答案 1 :(得分:6)
您考虑列表推导的方式并不完全正确。当你看到理解
y <- [f a | a <- x]
你不应该考虑将连续结果添加到(最初为空)结果列表的前面。
相反,请考虑每个f a
被添加到x
的其余部分上运行列表推导的结果的前面。也就是说,
[f a | a <- x:xs] == f x : [f a | a <- xs]
这是一种递归方法 - 通过假设列表尾部存在结果列表,您可以将下一个结果添加到其前面。
答案 2 :(得分:4)
有必要仔细研究列表推理如何对monadic计算进行描述,而不是直觉地使用它们的工作方式。
[a | a <- [1..3]] == do {a <- [1..3]; return a} -- desugared comprehension
== [1..3] >>= return a -- desugared do
== concat (map return [1..3]) -- definition of >>=
== concat [[1], [2], [3]] -- defintiion of return
== [1,2,3] -- definition of concat