如果你写:
let x = [1] ++ undefined ++ [3]
然后x
不计算。它存储为必须进行评估的表达式。但是,你查询x
。换句话说,你想要show x
。现在为了显示列表,必须评估整个列表(及其元素)。
现在毗连运算(++) :: [a] -> [a] -> [a]
定义为:
(++) :: [a] -> [a] -> [a]
(++) (x:xs) ys = x : xs ++ ys
(++) [] ys = ys
例如[1,2] ++ [3,4]
:将导致:
[1,2] ++ [3,4]
-> 1 : ([2] ++ [3,4])
-> 1 : 2 : ([] ++ [3,4])
-> 1 : 2 : [3,4]
或undefined
工作时:
[1,2] ++ undefined
-> 1 : ([2] ++ undefined)
-> 1 : 2 : ([] ++ undefined)
-> 1 : 2 : undefined
注意这是懒惰地完成:如果你需要更多的元素,你只能进一步concat。此外(++)
确实不是关心单个元素:如果一个元素包含一个计算上昂贵的操作,那么该操作被推迟直到有必要计算它为止。
所以只要第一个列表仍然可以发出元素,我们不关心第二个列表。当第一个列表结束时,我们只需返回第二个列表。
我们看到,这部分的工作原理:解释打印[1*** Exception: Prelude.undefined
所以在操作者(++)
的评价,[1]
的第一元件被发射,评估和打印。但后来我们达到undefined
,并且Haskell无法处理它以打印它(它的元素)并终止。
如果你写在另一方面let x = [undefined,undefined,undefined]
和你打电话length x
,然后列表的元素不被评估。 length
的定义如下:在该函数
length :: [a] -> Int
length [] = 0
length (_:xs) = 1 + length xs
头脑下划线(_
)。length
不关心个别元素。它将简单地遍历列表直到它遇到空列表[]
,并返回该计数,所以3
。
如果在另一方面,你会写
length $ [1] ++ undefined ++ [2,3]
,你会得到同样的问题:
Prelude> length $ [1] ++ undefined ++ [2,3]
*** Exception: Prelude.undefined
因为为了在列表中行走,我们必须Concat的[1]
与undefined
。
请注意,这两个表达式是不一样的。为简洁起见,假设'u = undefined':[u,u,u]'是'[u] ++ [u] ++ [u]',而不是'[u] ++ u ++ [u]'。在你的第一个例子中,你有一个未定义的*列表*,而在第二个例子中,你只有未定义的*元素*,所以'长度'工作正常。 –