【问题标题】:Why in Haskell maximum (8,1) = 1? [duplicate]为什么在 Haskell 中最大值 (8,1) = 1? [复制]
【发布时间】:2019-11-06 10:32:17
【问题描述】:

我刚刚开始学习 Haskell。据我所知maximum 给出了整数列表的最大值。所以,maximum [2,5,7,1] 给出 7。但是为什么通过给出一个元组输入,最大值总是给出第二个元素?例如,maximum (8,1) 给出 1。sum (8,1)product (5,2)minimum (4,5)... 都给出了元组的第二个元素。那么,任何人都可以向初学者解释为什么会发生这种情况吗?

【问题讨论】:

  • 一个关键的问题是,对于 元组,允许 2 个元素的类型不同,因此 (8, True) 和 (True, 8) 是完全合法的Haskell 表达式。所以没有通用的方法来定义一个既通用又直观地令人满意的最大函数。对于lists,所有元素必须是同一类型,所以[True, 8] 是非法的,而maximum [8,1] 和maximum [1,8] 都是合法的并且计算结果为8 ,正如您直觉所期望的那样。
  • 元组不是列表意义上的序列。它们是带有多值标签的单值。 (8, 1) 可以认为是1,带有8 的标签; (1,2,3,4,5) 可以被认为是一个5,具有四个可区分标签1234

标签: haskell max foldable


【解决方案1】:

简短回答:对于 2 元组,Foldable 实例(仅)考虑第二项。因此,maximum 函数将始终返回 2 元组的第二项。

因为 2 元组是 Foldable 的一个实例。确实是defined as [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)

所以这里我们用y(作为x 参数)调用mf,将Nothing 作为mcase … of …Nothing 匹配并返回 x。因此 2 元组的最大值定义为:

maximum (_, y) = fromMaybe (error "…") (Just y)

因此更短:

-- maximum for a (a, b)
maximum (_, y) = y

【讨论】:

  • @TheInnerLight:它不能同时考虑这两个值,因为Foldable 的实例应该是那种* -> *,所以instance Foldable (,) 会“崩溃”,因为它具有同样的属性* -> * -> *.
  • @TheInnerLight 这个('qwerty', 12345) 的最大值是多少?定义任何元组的最大值实际上是没有意义的。但是您始终可以为 (Int, Int) 类型的元组定义自己的 maxTuple 函数
  • @TheInnerLight 因为类型不同,一般来说。例如maximum ("hello", 4) 不能同时考虑这两个元素。这里的类型是(,) String IntFoldable 将其视为类似容器的类型(,) String,恰好有Int 作为其元素。我不太相信在库中允许 Foldable ((,) a) 是个好主意,但如果我们允许这样做,我们就没有类型安全的选项,只能考虑对的第二个组件。
  • @chi:我也没有。老实说,我认为 2 元组可能不应该是 Foldable,因为它会造成很多混乱。是的,我们可以将 2 元组视为某种“带有上下文的值”(该上下文是第一项),但它可能会引起很多混乱。
  • 我认为Foldable 实例很好不是因为你经常想要折叠一个单元素容器,而是因为它强调了一个事实,一个元组不仅仅是一个小数列表上的变化。 maximum (8,1) 不返回 8 的事实应该迫使你停下来问问自己为什么首先有一个元组。
【解决方案2】:

Haskell 中的元组不像 Python 中那样是多值容器。相反,它们更接近于 单值 值容器,例如 Maybe aEither b a:具有上下文的值。虽然MaybeEither 都带有可能没有值的概念(Either 只是提供了比Maybe 更多的关于缺少值的信息),但元组带有关于值本身的信息的上下文。

(8, 1) 这样的值不包含两个值81。相反,8 一个包含 1 的容器的一部分。

因此,元组是可折叠的,即使这样做看起来微不足道。将(a, b) 类型的值减少到b 类型的值只需要返回b 类型的值,而不必担心如何处理b 类型的其他值,因为有 t 任何。

>>> maximum (Just 5)
5
>>> minimum (Just 5)
5

>>> maximum (Right 5)
5
>>> minimum (Right 5)
5

>>> maximum (True, 5)
5
>>> minimum (True, 5)
5

MaybeEither 相比,这些函数是total 元组:

>>> 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

【讨论】:

    猜你喜欢
    • 2011-04-19
    • 2023-04-09
    • 1970-01-01
    • 2022-01-23
    • 2015-06-12
    • 2023-01-26
    • 2014-08-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多