【问题标题】:How does haskell create a new list from another list?haskell 如何从另一个列表创建一个新列表?
【发布时间】:2016-08-18 11:02:12
【问题描述】:
假设我们有一个列表
x = [1..10]
我们打算以这种方式创建另一个列表 y:
y= [a|a<-x]
因此,在从x 创建列表y 时,它会访问x(从1 到10)的每个元素,并以相同的顺序将其插入y。由于 haskell 中的列表是单链表,我们只能在其头部插入一个新元素。所以首先它将 1 插入到[] 并且我们有[1]。然后它将 2 插入到它的头部 & 所以我们有[2,1]。然后它插入 3 & 我们有 [3,2,1] 等等。所以最终我们应该得到y 作为[10,9..1]。但相反,我们得到y 为[1..10]。为什么会这样?
【问题讨论】:
标签:
list
haskell
list-comprehension
lazy-evaluation
【解决方案1】:
因为它将它们“插入”到列表的尾部(在构建列表时),而不是头部(参见“尾部递归模数”):
[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
【解决方案2】:
您对列表推导的思考方式并不完全正确。当你看到理解时
y <- [f a | a <- x]
您不应该考虑将连续结果添加到(最初为空)结果列表的前面。
相反,请考虑将每个f a 添加到对x 的其余部分运行列表推导的结果的前面。也就是说,
[f a | a <- x:xs] == f x : [f a | a <- xs]
这是一种递归方法 - 通过假设结果列表存在于列表的尾部,您可以将下一个结果添加到它的前面。
【解决方案3】:
看看列表推导式是如何去糖化单子计算的,而不是使用直觉来了解它们是如何工作的,这很有启发意义。
[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