我正在通过 Graham Hutton 的书"Programming in Haskell"中的练习,作为一项练习的一部分,我重新实现了 Haskell 的last
函数,如下所示:
lasst xs = drop (length xs - 1) xs
现在这适用于非空列表:
> lasst [1,2,3,4,5]
[5]
但是,令我惊讶的是,对于空列表,它返回一个空列表:
> lasst []
[]
我对此感到惊讶,因为我希望(length []) - 1
被评估为-1
,随后对要抛出的drop -1 []
进行评估。
为什么我的实现为空列表返回空列表,而不是引发异常?
Haskell报告'10指定了标准的前奏曲。在本节中,我们将看到:
drop :: Int -> [a] -> [a] drop n xs |n <= 0= xs drop _ [] = [] drop n (_:xs) = drop (n-1) xs
所以对于负n
,它将返回整个列表。这对于drop
的文档是有意义的:
drop n xs
返回前n
元素后的后缀xs
,如果n > length xs
,则返回[]
。
因此,列表的前-1
元素根本不是元素。
这在drop
的例子之一中进一步介绍:
drop (-1) [1,2] == [1,2]
drop
和take
是总函数:无论(总(参数是什么,它们总是返回一些东西而不会导致运行时错误。他们的定义使它使
take k xs ++ drop k xs == xs
每个k
和(有限(xs
都成立。注意k
可以是负数,甚至大于xs
的长度,以上还是有保证的。
起初可能会令人惊讶,但他们有以下行为。假设xs = [1,2,3]
.然后
k take drop
==========================
...
-2 [] [1,2,3]
-1 [] [1,2,3]
0 [] [1,2,3]
1 [1] [2,3]
2 [1,2] [3]
3 [1,2,3] []
4 [1,2,3] []
5 [1,2,3] []
...
就个人而言,我不确定它们的整体性是否是一个好主意。对于负k
或大于长度k
,它们会导致运行时错误是有意义的。不过,这就是Haskell所做的。
(顺便注意,tail xs
和drop 1 xs
在xs=[]
时会有所不同,因为tail
不是总的。