我正在寻找一个函数,它接受一个函数 (a -> a -> a) 和一个 [Maybe a] 列表并返回 Maybe a。胡格尔没有给我任何有用的东西。这看起来是一个很常见的模式,所以我想问这种情况是否有最佳实践?
>>> f (+) [Just 3, Just 3]
Just 6
>>> f (+) [Just 3, Just 3, Nothing]
Nothing
提前致谢,克里斯
最佳答案
您应该首先打开 [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 ;等效于 fmap
或 liftM
)将二进制函数折叠到列表上:
GHCi> foldl' (+) 0 <$> sequence [Just 1, Just 2]
Just 3
当然,您可以使用任何您想要的折叠,例如 foldr
, foldl1
等等
作为额外的,如果您希望结果为 Nothing
当列表为空时,从而能够省略折叠的零值而不用担心空列表上的错误,那么你可以使用这个折叠函数:
mfoldl1' :: (MonadPlus m) => (a -> a -> a) -> [a] -> m a
mfoldl1' _ [] = mzero
mfoldl1' f (x:xs) = return $ foldl' f x xs
foldr
也类似, foldl
等。您需要导入 Control.Monad为此。
但是,使用方式必须略有不同:
GHCi> mfoldl1' (+) =<< sequence [Just 1, Just 2]
Just 3
或
GHCi> sequence [Just 1, Just 2] >>= mfoldl1' (+)
Just 3
这是因为,与其他折叠不同,结果类型看起来像 m a
而不是a
;它是一个绑定(bind)而不是映射。
关于haskell - 如何评估 Maybe 列表的最佳实践,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8544278/