如何评估Maybes列表的最佳实践

时间:2011-12-17 11:05:56

标签: haskell monads

我正在寻找一个函数,它接受一个函数(a - > a - > a)和一个[Maybe a]列表并返回Maybe a。 Hoogle没有给我任何帮助。这看起来像一个非常常见的模式,所以我问这个案例是否有最好的做法?

>>> f (+) [Just 3, Just 3]
Just 6
>>> f (+) [Just 3, Just 3, Nothing]
Nothing

先谢谢你,克里斯

3 个答案:

答案 0 :(得分:26)

您应首先将[Maybe a]变为Maybe [a],其中包含所有Just元素(如果其中任何一个Nothing,则会产生Nothing)。 这可以使用sequence使用Maybe的Monad实例来完成:

GHCi> sequence [Just 1, Just 2]
Just [1,2]
GHCi> sequence [Just 1, Just 2, Nothing]
Nothing

definition of sequence等同于以下内容:

sequence [] = return []
sequence (m:ms) = do
  x <- m
  xs <- sequence ms
  return (x:xs)

所以我们可以将后一个例子扩展为:

do x <- Just 1
   xs <- do
       y <- Just 2
       ys <- do
           z <- Nothing
           zs <- return []
           return (z:zs)
       return (y:ys)
   return (x:xs)

使用do-notation expression of the monad laws,我们可以按如下方式重写:

do x <- Just 1
   y <- Just 2
   z <- Nothing
   return [x, y, z]

如果您知道Maybe monad是如何工作的,那么您现在应该了解sequence如何实现所需的行为。 :)

然后,您可以使用foldr使用(<$>)(来自Control.Applicative;等效地,fmapliftM)将此二进制函数折叠到列表中:

GHCi> foldl' (+) 0 <$> sequence [Just 1, Just 2]
Just 3

当然,您可以使用任何您想要的折叠,例如foldrfoldl1等。

作为额外的,如果你希望当列表为空时结果为Nothing,从而能够省略折叠的零值而不必担心空列表上的错误,那么你可以使用这个折叠功能:

mfoldl1' :: (MonadPlus m) => (a -> a -> a) -> [a] -> m a
mfoldl1' _ [] = mzero
mfoldl1' f (x:xs) = return $ foldl' f x xs

,同样适用于foldrfoldl等。您需要为此导入Control.Monad

然而,必须略微区别使用:

GHCi> mfoldl1' (+) =<< sequence [Just 1, Just 2]
Just 3

GHCi> sequence [Just 1, Just 2] >>= mfoldl1' (+)
Just 3

这是因为,与其他折叠不同,结果类型看起来像m a而不是a;它是 bind 而不是地图

答案 1 :(得分:14)

根据我的理解,你想获得一堆maybes的总和,或Nothing如果其中任何一个是Nothing。这实际上非常简单:

maybeSum = foldl1 (liftM2 (+))

您可以将其概括为:

f :: Monad m => (a -> a -> a) -> [m a] -> m a
f = foldl1 . liftM2

Maybe monad一起使用时,f完全符合您的要求。

如果您关心空列表,可以使用此版本:

f :: MonadPlus m => (a -> a -> a) -> [m a] -> m a
f _ []      = mzero
f fn (x:xs) = foldl (liftM2 fn) x xs

答案 2 :(得分:6)

如此简单的事情:

λ Prelude > fmap sum . sequence $ [Just 1, Just 2]
Just 3
λ Prelude > fmap sum . sequence $ [Just 1, Just 2, Nothing]
Nothing

或者,使用(+)

λ Prelude > fmap (foldr (+) 0) . sequence $ [Just 1, Just 2]
Just 3
λ Prelude > fmap (foldr (+) 0) . sequence $ [Just 1, Just 2, Nothing]
Nothing

所以,maybeSum = fmap sum . sequence