我刚刚开始学习 Haskell。据我所知maximum
,给出了整数列表的最大值。所以,maximum [2,5,7,1]
给出 7。但是为什么通过给出一个元组输入,最大值总是给出第二个元素?例如,maximum (8,1)
给出 1。同样的事情发生在sum (8,1)
, product (5,2)
, minimum (4,5)
... 都给出元组的第二个元素。那么,任何人都可以向初学者解释为什么会发生这种情况吗?
2 回答
简短回答:对于 2 元组,Foldable
实例(仅)考虑第二项。因此,该maximum
函数将始终返回 2 元组的第二项。
因为 2 元组是Foldable
. 实际上,它被定义为 [src]:
instance Foldable ((,) a) where foldMap f (_, y) = f y foldr f z (_, y) = f y z
maximum
本质上是一种折叠模式。它等效于:
maximum = foldr1 max
其中foldr1
实施为:
foldr1 f = fromMaybe (error "…") . foldr mf Nothing
where mf x m = Just (case m of
Nothing -> x
Just y -> f x y)
所以这意味着对于一个 2 元组,它将被实现为:
maximum (_, y) = fromMaybe (error "…") (mf y Nothing)
where mf x m = Just (case m of
Nothing -> x
Just y -> max x y)
所以这里我们mf
用y
(作为x
参数)和Nothing
as调用m
。case … of …
this 匹配并Nothing
返回x
。因此 2 元组的最大值定义为:
maximum (_, y) = fromMaybe (error "…") (Just y)
因此更短:
-- maximum for a (a, b)
maximum (_, y) = y
Haskell 中的元组不像 Python 中那样是多值容器。相反,它们更接近于单值容器,例如Maybe a
or Either b a
:带有上下文的值。虽然两者都Maybe
带有Either
可能没有值的概念(Either
只是提供比Maybe
缺少值更多的信息),但元组带有关于值本身的信息的上下文。
像(8, 1
) 这样的值不包含两个值8
和1
。相反,8
它是包含1
.
因此,元组是可折叠的,即使这样做看起来微不足道。将 type 的值减少为(a, b)
的值b
只需返回 type 的值b
,而不用担心如何处理 type 的其他值b
,因为没有任何值。
>>> maximum (Just 5)
5
>>> minimum (Just 5)
5
>>> maximum (Right 5)
5
>>> minimum (Right 5)
5
>>> maximum (True, 5)
5
>>> minimum (True, 5)
5
但是,与or相比,此类函数与元组是完全的:Maybe
Either
>>> maximum Nothing
*** Exception: maximum: empty structure
>>> maximum (Left 5)
*** Exception: maximum: empty structure
无论元组包含多少类型,除了最右边的类型之外,所有类型都将固定用于Foldable
.
-- Actual instance for (a, b)
instance Foldable ((,) a) where
foldMap f (_, y) = f y
foldr f z (_, y) = f y z
-- Instance for (a, b, c) if you wanted it
instance Foldable ((,,) a b) where
foldMap f (_, _, y) = f y
foldr f z (_, _, y) = f y z