haskell如何从另一个列表创建新列表?

时间:2016-08-18 11:02:13

标签: list haskell list-comprehension lazy-evaluation

假设我们有一个列表

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]。为什么会这样?

3 个答案:

答案 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