我正在尝试理解以下功能:
q1 :: [Int] -> Int
q1 [] = 0
q1 [x] = x
q1 (x:_:xs) = max x (q1 xs)
当输入这个:q1(地图abs [-1,-6,-5,7])时,它得到了我5.有人可以告诉我为什么会这样吗?我理解map函数如何,但模式匹配(x:_xs)有点令人困惑。谢谢!
Haskell中的列表 - 至少在概念上 - 是一个链表。有两种可能性:
[]
;要么(x:xs)
,其中x
是头(第一项),xs
是尾部(列表的其余部分)。Haskell也使用了语法糖。例如,[1]
在幕后翻译为(1:[])
,[1,4,2,5]
翻译为(1:(4:(2:(5:[]))))
。
为什么这很重要?我们首先会尝试了解q1
函数。如果我们查看类型,我们会看到q1
将Int
s列表作为输入,并返回Int
。它以递归方式定义为:
q1 :: [Int] -> Int
q1 [] = 0
q1 [x] = x
q1 (x:_:xs) = max x (q1 xs)
这意味着q1
为空列表为零(0
); q1
列表中有一个元素x
是x
。对于具有两个或更多元素的列表,该列表maximum
的第一项的x
和该列表尾部的尾部。这是因为我们模式匹配(x:_:xs)
的缩写(x:(_:xs))
。下划线基本上意味着“不在乎”。因此列表应该是尾部也是缺点的缺点,我们对列表x
的头部以及列表xs
尾部的尾部感兴趣。
如果我们对此进行推理,我们就会发现q1
返回奇数索引处的元素的最大值(所以第一个,第三个,第五个等元素)。如果列表具有偶数长度,我们还计算最大值为零(因此,如果奇数索引处的所有元素都是负数,则函数将返回零,但这仅在我们具有偶数长度列表的情况下)。
现在,如果用q1 (map abs [-1,-6,-5,7])
来称呼它,那么就意味着我们将在q1
上对map abs
的结果调用[-1, -6, -5, 7]
。 map abs
构造了一个列表,其中abs
应用于列表的所有元素(尽管它是懒惰地应用)。所以在map abs [-1, -6, -5, 7]
之后,我们获得了[1, 6, 5, 7]
列表。现在奇数指数的元素是1
和5
。所以q1
将计算这些元素的最大值和零(因为列表的长度是4,这是偶数)。并且max(0,1,5)是5。
就个人而言,尤其是我们也认为零,但仅在列表具有均匀长度的情况下,这是非常“不稳定”的事实。它可能导致难以理解的错误,因为它可能是函数细节的结果。我们可以例如用零计算最大值,而不管列表的长度如何:
q2 :: (Num a, Ord a) => [a] -> a
q2 [] = 0
q2 [x] = max 0 x
q2 (x:_:xs) = max x (q2 xs)
或者我们可以决定不使用零,而不是在空列表上定义最大值,例如:
q3 :: Ord a => [a] -> a
q3 [x] = x
q3 [x,_] = x
q3 (x:_:xs) = max x (q3 xs)