我尝试编写类似于maximum
中的广义Prelude
函数。我的第一个naiv方法看起来像这样:
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'
函数才能使其正常工作?
答案 0 :(得分:9)
正如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
即。您可以比较值xs
与mempty
。请注意,我们需要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
答案 1 :(得分:4)
有几种方法可以做到这一点(@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)
答案 2 :(得分: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)
答案 3 :(得分:2)
正如许多人已经告诉过你的那样,你不能对一个值进行模式匹配。
正如人们告诉你的那样,模式匹配可以说是Haskell等同于Java语言中的对象字段:它对于紧密耦合代码的内部消费很有价值,但可能不是你希望暴露给外部客户端代码的东西。基本上,如果你让一段代码知道你的类型的构造函数,那么现在你永远不能在不改变其他代码的情况下改变这些构造函数 - 即使你的类型的语义没有真正改变。
这里最好的解决方案就是使用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)
请注意,foldr
是Foldable
个实例的通用析构函数或消除器:它的两个参数是“如何处理非清空Foldable
“和”如何处理mempty
。这比模式匹配更抽象,更可重用。
答案 4 :(得分: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
,并允许推断其类型。