4

我正在处理一些缺少值的数据,这些数据仅表示为 Maybe 值列表。我想执行各种聚合/统计操作,这些操作只是忽略缺失值。

这与以下问题有关:

在haskell中总结Maybe Int列表的惯用方法

如何轻松地使用可能的幺半群并将值与自定义操作结合起来?

Nothing但是,如果缺少任何值,前一个问题就满足于返回,这在我的情况下不是一个选项。我有一个解决方案,其中涉及NumMaybe. 但是,这意味着它特定于加法和乘法,并且它也有一些其他问题。

instance Num a => Num (Maybe a) where
  negate      = fmap negate
  (+)         = liftA2 (+)
  (*)         = liftA2 (*)
  fromInteger = pure . fromInteger
  abs         = fmap abs
  signum      = fmap signum

基于此,我们可以这样做:

maybeCombineW :: (a -> a -> a) -> Maybe a -> Maybe a -> Maybe a
maybeCombineW f (Just x)  (Just y)  = Just (f x y)
maybeCombineW _ (Just x)  Nothing   = Just x
maybeCombineW _ Nothing   (Just y)  = Just y
maybeCombineW _ Nothing   Nothing   = Nothing


maybeCombineS :: (a -> a -> a) -> Maybe a -> Maybe a -> Maybe a
maybeCombineS f (Just x)  (Just y)  = Just (f x y)
maybeCombineS _ _          _        = Nothing


class (Num a) => Num' a where
  (+?) :: a -> a -> a
  (*?) :: a -> a -> a
  (+!) :: a -> a -> a
  (*!) :: a -> a -> a
  (+?) = (+)
  (*?) = (*)
  (+!) = (+)
  (*!) = (*)

instance {-# OVERLAPPABLE  #-} (Num a) => Num' a
instance {-# OVERLAPPING  #-} (Num' a) => Num' (Maybe a) where
  (+?) = maybeCombineW (+?)
  (*?) = maybeCombineW (*?)
  (+!) = maybeCombineS (+!)
  (*!) = maybeCombineS (*!)


sum' :: (Num' b, Foldable t) => t b -> b
sum' = foldr (+?) 0

sum'' :: (Num' b, Foldable t) => t b -> b
sum'' = foldr (+!) 0

我喜欢这个:它给了我两个函数,一个 lenientsum'和一个 strict sum'',我可以根据需要从中选择。我可以使用相同的函数来汇总任何Num实例,因此我可以为列表重用相同的代码,而Maybe无需先转换它们。

我不喜欢这个:实例重叠。此外,对于除加法和乘法之外的任何操作,我必须指定一个新类型类并创建新实例。

因此,我想知道是否有可能以某种方式获得一个很好的通用解决方案,也许按照第二个问题中建议的思路,将其Nothing视为mempty有问题的操作。

有没有一种很好的惯用方式来做到这一点?

编辑:这是迄今为止最好的解决方案:

inout i o = ((fmap o) . getOption) . foldMap (Option . (fmap i))
sum' = Sum `inout` getSum
min' = Min `inout` getMin
-- etc.
4

2 回答 2

6

有一个例子Monoid完全正确:

instance Monoid a => Monoid (Maybe a) where
  mempty = Nothing
  Nothing `mappend` m = m
  m `mappend` Nothing = m
  Just m1 `mappend` Just m2 = Just (m1 `mappend` m2)

它在Data.Monoid

因此,

foldMap (liftA Sum) [Just 1, Nothing, Just 2, Nothing, Just 3] = 
   fold [Just (Sum 1), Nothing, Just (Sum 2), Nothing, Just (Sum 3)] = 
      Just (Sum 6)

对于严格的左折叠版本,而不是fold一个可以使用foldl' mappend mempty,而不是foldMap f使用foldl' (mappend . f) mempty。在Maybe幺半群中,memptyNothing

于 2016-10-06T10:22:01.930 回答
1

仅使用catMaybesfromData.Maybe丢弃所有Nothing值怎么样?然后,您可以在纯值列表上运行任何聚合和计算。

于 2016-10-06T11:04:42.750 回答