7

我尝试编写一个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'函数以使其工作?

4

5 回答 5

11

正如 CA 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
于 2012-08-31T14:17:20.033 回答
5

有几种方法可以做到这一点(@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)
于 2012-08-31T14:23:30.147 回答
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)
于 2012-08-31T14:22:58.930 回答
2

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

正如很少人告诉你的那样,模式匹配可以说是 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)

Note that foldr is a generalized destructor or eliminator for Foldable instances: its two arguments are "what to do with a non-empty Foldable" and "what to do with mempty. This is more abstract and reusable than pattern matching.

于 2012-08-31T17:22:46.850 回答
1

怎么样

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, 并允许推断其类型。

于 2012-08-31T14:17:23.697 回答