折叠 Maybe (Monoid) 的 Foldable,忽略 Haskell 中的缺失值
Fold a Foldable of Maybe (Monoid) ignoring the missing values in Haskell
我正在处理一些具有缺失值的数据,这些数据简单地表示为可能值的列表。我想执行各种 aggregates/statistical 操作,这些操作只是忽略缺失值。
这与以下问题有关:
Idiomatic way to sum a list of Maybe Int in haskell
How to use the maybe monoid and combine values with a custom operation, easily?
但是,如果缺少任何值,前一个问题满足于返回 Nothing
,这在我的情况下不是一个选项。我有一个解决方案,涉及为 Maybe
创建一个 Num
实例。然而,这意味着它是特定于加法和乘法的,它也有一些其他问题。
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
我喜欢这个的地方:它给了我两个功能,一个宽松的 sum'
和一个严格的 sum''
,我可以根据需要从中选择。我可以使用相同的函数对任何 Num
个实例求和,因此我可以在没有 Maybe
的情况下对列表重复使用相同的代码,而无需先转换它们。
我不喜欢这个:实例重叠。此外,对于加法和乘法以外的任何操作,我必须指定一个新类型 class 并创建新实例。
因此,我想知道是否有可能以某种方式获得一个不错的通用解决方案,也许按照第二个问题中建议的思路,将 Nothing
视为任何操作的 mempty
有问题。
是否有一个很好的惯用方法来做到这一点?
编辑:这是迄今为止最好的解决方案:
inout i o = ((fmap o) . getOption) . foldMap (Option . (fmap i))
sum' = Sum `inout` getSum
min' = Min `inout` getMin
-- etc.
有一个 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)
对于严格的左折叠版本,可以使用 foldl' mappend mempty
代替 fold
,使用 foldl' (mappend . f) mempty
代替 foldMap f
。在 Maybe
幺半群中,mempty
是 Nothing
.
如何仅使用 Data.Maybe
中的 catMaybes
来丢弃所有 Nothing
值?然后,您可以 运行 对纯值列表进行任何聚合和计算。
我正在处理一些具有缺失值的数据,这些数据简单地表示为可能值的列表。我想执行各种 aggregates/statistical 操作,这些操作只是忽略缺失值。
这与以下问题有关:
Idiomatic way to sum a list of Maybe Int in haskell
How to use the maybe monoid and combine values with a custom operation, easily?
但是,如果缺少任何值,前一个问题满足于返回 Nothing
,这在我的情况下不是一个选项。我有一个解决方案,涉及为 Maybe
创建一个 Num
实例。然而,这意味着它是特定于加法和乘法的,它也有一些其他问题。
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
我喜欢这个的地方:它给了我两个功能,一个宽松的 sum'
和一个严格的 sum''
,我可以根据需要从中选择。我可以使用相同的函数对任何 Num
个实例求和,因此我可以在没有 Maybe
的情况下对列表重复使用相同的代码,而无需先转换它们。
我不喜欢这个:实例重叠。此外,对于加法和乘法以外的任何操作,我必须指定一个新类型 class 并创建新实例。
因此,我想知道是否有可能以某种方式获得一个不错的通用解决方案,也许按照第二个问题中建议的思路,将 Nothing
视为任何操作的 mempty
有问题。
是否有一个很好的惯用方法来做到这一点?
编辑:这是迄今为止最好的解决方案:
inout i o = ((fmap o) . getOption) . foldMap (Option . (fmap i))
sum' = Sum `inout` getSum
min' = Min `inout` getMin
-- etc.
有一个 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)
对于严格的左折叠版本,可以使用 foldl' mappend mempty
代替 fold
,使用 foldl' (mappend . f) mempty
代替 foldMap f
。在 Maybe
幺半群中,mempty
是 Nothing
.
如何仅使用 Data.Maybe
中的 catMaybes
来丢弃所有 Nothing
值?然后,您可以 运行 对纯值列表进行任何聚合和计算。