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

Chr*_*ris 16 monads haskell

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

>>> f (+) [Just 3, Just 3]
Just 6
>>> f (+) [Just 3, Just 3, Nothing]
Nothing
Run Code Online (Sandbox Code Playgroud)

谢谢,克里斯

ehi*_*ird 26

你应该先把所有元素[Maybe a]变成a (如果有的话会产生).这可以使用Maybe的Monad实例使用序列完成:Maybe [a]JustNothingNothing

GHCi> sequence [Just 1, Just 2]
Just [1,2]
GHCi> sequence [Just 1, Just 2, Nothing]
Nothing
Run Code Online (Sandbox Code Playgroud)

序列定义等同于以下内容:

sequence [] = return []
sequence (m:ms) = do
  x <- m
  xs <- sequence ms
  return (x:xs)
Run Code Online (Sandbox Code Playgroud)

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

do x <- Just 1
   xs <- do
       y <- Just 2
       ys <- do
           z <- Nothing
           zs <- return []
           return (z:zs)
       return (y:ys)
   return (x:xs)
Run Code Online (Sandbox Code Playgroud)

使用monad定律do-notation表达式,我们可以重写如下:

do x <- Just 1
   y <- Just 2
   z <- Nothing
   return [x, y, z]
Run Code Online (Sandbox Code Playgroud)

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

然后,您可以foldr使用(<$>)(来自Control.Applicative ;等效,fmapliftM)来组合这个以在列表上折叠二进制函数:

GHCi> foldl' (+) 0 <$> sequence [Just 1, Just 2]
Just 3
Run Code Online (Sandbox Code Playgroud)

当然,您可以使用任何您想要的折叠,例如foldr,foldl1等等.

另外,如果您希望结果为Nothing列表为空时,因此可以省略折叠的零值而不必担心空列表上的错误,那么您可以使用此折叠函数:

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

以及类似的foldr,foldl等等.您需要为此导入Control.Monad.

但是,必须使用略有不同的方法:

GHCi> mfoldl1' (+) =<< sequence [Just 1, Just 2]
Just 3
Run Code Online (Sandbox Code Playgroud)

要么

GHCi> sequence [Just 1, Just 2] >>= mfoldl1' (+)
Just 3
Run Code Online (Sandbox Code Playgroud)

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


Tik*_*vis 14

据我所知,你想得到一堆maybes的总和,或者Nothing是否有任何一个Nothing.这实际上非常简单:

maybeSum = foldl1 (liftM2 (+))
Run Code Online (Sandbox Code Playgroud)

您可以将此概括为:

f :: Monad m => (a -> a -> a) -> [m a] -> m a
f = foldl1 . liftM2
Run Code Online (Sandbox Code Playgroud)

Maybemonad 一起使用时,f可以按照您想要的方式工作.

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

f :: MonadPlus m => (a -> a -> a) -> [m a] -> m a
f _ []      = mzero
f fn (x:xs) = foldl (liftM2 fn) x xs
Run Code Online (Sandbox Code Playgroud)

  • 对于空列表返回"Nothing"似乎与我违反直觉,我更喜欢`foldl`和零元素,否则它将在一个中混合两个不同的情况.BTW,另一种变化是'foldl1(liftA2(+))[Just 1,Just 10,Just 100] (2认同)

lbo*_*lla 6

简单的事情怎么样:

? Prelude > fmap sum . sequence $ [Just 1, Just 2]
Just 3
? Prelude > fmap sum . sequence $ [Just 1, Just 2, Nothing]
Nothing
Run Code Online (Sandbox Code Playgroud)

或者,通过使用(+):

? Prelude > fmap (foldr (+) 0) . sequence $ [Just 1, Just 2]
Just 3
? Prelude > fmap (foldr (+) 0) . sequence $ [Just 1, Just 2, Nothing]
Nothing
Run Code Online (Sandbox Code Playgroud)

所以,maybeSum = fmap sum . sequence.

  • 刚注意到第三个答案已包含此版本!在阅读ehird的回复时,一看到"MonadPlus"这个词,我就会说:"必须有一个更简单的方法",我没有打扰继续:对不起!无论如何,我赞成了第三个回答. (2认同)