我正在寻找一个函数,它接受一个函数(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]
Just
Nothing
Nothing
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 ;等效,fmap
或liftM
)来组合这个以在列表上折叠二进制函数:
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)
与Maybe
monad 一起使用时,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)
简单的事情怎么样:
? 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
.