【问题标题】:Monoid mempty in pattern matching模式匹配中的 Monoid mempty
【发布时间】:2012-08-31 13:58:04
【问题描述】:

我尝试编写一个通用的maximum 函数,类似于Prelude 中的函数。我的第一个天真的方法如下所示:
maximum' :: (F.Foldable a, Ord b) => a b -> Maybe b
maximum' mempty = Nothing
maximum' xs = Just $ F.foldl1 max xs

但是,当我测试它时,无论输入如何,它总是返回 Nothing
> maximum' [1,2,3]
> Nothing

现在我想知道是否可以获取 Monoid 类型实例的空值。我写的一个测试函数工作正常:
getMempty :: (Monoid a) => a -> a
getMempty _ = mempty

> getMempty [1,2,3]
> []

我已经看过这两个问题,但我不知道答案如何解决我的问题:
Write a Maximum Monoid using Maybe in Haskell
Haskell Pattern Matching on the Empty Set

我将如何重写 maximum' 函数以使其工作?

【问题讨论】:

  • 您的第一种方法失败了,因为您不能对任意值进行模式匹配,只能对构造函数进行模式匹配。您将参数绑定到一个名为mempty 的变量,它会影响Monoid 中定义的变量。您链接的第二个问题涉及类似的问题。如何最好地实现您的实际目标是另一回事。

标签: haskell pattern-matching monoids


【解决方案1】:

正如 C. A. McCann 在他的评论中指出的那样,你不能对值进行模式匹配,只能对模式进行匹配。

方程maximum' mempty = Nothing实际上等价于方程maximum' x = Nothing。参数绑定到名称并返回 Nothing

这是使您的代码工作的一种方法:

maximum' :: (F.Foldable a, Ord b, Eq (a b), Monoid (a b)) => a b -> Maybe b
maximum' xs
  | xs == mempty = Nothing
  | otherwise    = Just $ F.foldl1 max xs

即您可以比较xsmempty。请注意,我们需要一个 Monoid 约束才能获得值 mempty :: a b 和一个 Eq 约束才能进行比较。

另一个更优雅的解决方案是使用折叠来区分空和非空情况:

maximum'' :: (F.Foldable a, Ord b) => a b -> Maybe b
maximum'' xs = F.foldl max' Nothing xs
  where max' Nothing x = Just x
        max' (Just y) x = Just $ max x y

【讨论】:

  • 您可以对文字进行模式匹配,文字是值而不是模式。
  • 其实字面量就是模式。例如,请参阅 haskell 报告中第 3.17.1 节中的语法:haskell.org/onlinereport/exps.html
  • @MrBones:文字“模式”是通过翻译成保护和相等检查定义的语法糖,如果您尝试将文字与 Num 实例匹配但没有 Eq 实例,您会发现。没有固有的理由不能将相同的翻译用于mempty 之类的东西,只是将此类伪模式与隐藏现有标识符的绑定区分开来。
  • 非常感谢,在 C. A. McCann 指出 Data.Monoid.mempty 的阴影之后,我有了同样的想法。
【解决方案2】:

有几种方法可以做到这一点(@opqdonut 演示的方法很好)。也可以在Maybe 周围创建一个“最大”幺半群,并使用foldMap

newtype Maximum a = Max { unMaximum :: Maybe a }

instance (Ord a) => Monoid (Maximum a) where
  mempty = Max Nothing
  mappend (Max Nothing) b = b
  mappend a (Max Nothing) = a
  mappend (Max (Just a)) (Max (Just b)) = Max . Just $ (max a b)

maximum' = unMaximum . F.foldMap (Max . Just)

【讨论】:

    【解决方案3】:

    有很多方法,一种是(正如您提到的)创建Monoid 的实例。但是,我们需要将其包装到Maybe 以区分我们没有值时的情况。实现可能如下所示:

    import Data.Monoid (Monoid, mempty, mappend)
    import qualified Data.Foldable as F
    
    -- Either we have a maximum value, or Nothing, if the
    -- set of values is empty.
    newtype Maximum a = Maximum { getMaximum :: Maybe a }
        deriving (Eq, Ord, Read, Show)
    
    instance Ord a => Monoid (Maximum a) where
        mempty                      = Maximum Nothing
    
        -- If one part is Nothing, just take the other one.
        -- If both have a value, take their maximum.
        (Maximum Nothing) `mappend` y    = y
        x `mappend` (Maximum Nothing)    = x
        (Maximum (Just x)) `mappend` (Maximum (Just y))
                                         = Maximum (Just $ x `max` y)
    
    
    maximum' :: (F.Foldable t, Ord a) => t a -> Maximum a
    maximum' = F.foldMap (Maximum . Just)
    

    【讨论】:

    • 哈哈哈,我们几乎在同一时间发布了几乎完全相同的答案! +1
    【解决方案4】:

    正如许多人已经告诉你的那样,你不能对一个值进行模式匹配。

    正如很少人告诉你的那样,模式匹配可以说是 Java 等语言中对象字段的 Haskell 等价物:它对紧密耦合代码的内部使用很有价值,但你可能不希望将其暴露给外部客户端代码。基本上,如果你让一段代码知道你的类型的构造函数,现在你永远无法在不更改另一段代码的情况下更改这些构造函数——即使你的类型的语义并没有真正改变。

    这里最好的解决方案就是使用Foldable.foldr

    maximum' :: (F.Foldable a, Ord b) => a b -> Maybe b
    maximum' = F.foldr step Nothing
        where step x Nothing = Just x
              step x (Just y) = Just (max x y)
    

    注意foldrFoldable 实例的通用析构函数消除器:它的两个参数是“如何处理非空Foldable " 和 "如何处理mempty。这比模式匹配更抽象和可重用。

    【讨论】:

    • 另一方面,如果您的类型的语义确实发生了变化,那么破坏其他代码是非常好的事情——而且在理想情况下,类型的表示将与语义一致,如 []Maybe
    【解决方案5】:

    怎么样

    maximum' :: (Monoid (t a), F.Foldable t, Ord a, Eq (t a)) => t a -> Maybe a
    maximum' xs
       | xs == mempty = Nothing
       | otherwise    = Just $ F.foldl1 max xs
    

    你少了一个守卫。

    getEmpty 函数中,您不需要它。只需使用mempty,并允许推断其类型。

    【讨论】:

    • 不幸的是,我认为这也需要Eq (a b) 约束。
    • 是的,当我实际尝试这个时,推断的类型签名是不同的。我会在几秒钟内改变它
    猜你喜欢
    • 2022-01-11
    • 1970-01-01
    • 2011-02-03
    • 1970-01-01
    • 1970-01-01
    • 2011-09-13
    • 2012-10-31
    • 2011-08-16
    • 2020-01-17
    相关资源
    最近更新 更多