我需要一个函数,它将[Maybe a]列表作为输入,获取每个值,处理它并返回Maybe [a].如果输入列表没有Nothing,我想返回Nothing.
func [Just 1,Just 2,Just 3,Just 4,Just 5] => this returns Just [1,2,3,4,5]
func [Just 1,Nothing,Just 3,Just 4,Just 5] => this returns Nothing
Run Code Online (Sandbox Code Playgroud)
我写了这个
func mlist = if elem Nothing mlist
then Nothing
else Just $ map (\(Just e) -> e) mlist
Run Code Online (Sandbox Code Playgroud)
它有效,但我想知道我是否能做得更好.我不喜欢我所做的部分elem Nothing mlist并再次映射mlist.
功能已存在此sequence :: Monad m => [m a] -> m [a]功能:
Prelude> import Control.Monad
Prelude Control.Monad> sequence [Just 3]
Just [3]
Prelude Control.Monad> sequence [Just 3, Nothing]
Nothing
Prelude Control.Monad> sequence [Just 3, Just 2]
Just [3,2]
Prelude Control.Monad> sequence [Just 1,Just 2,Just 3,Just 4,Just 5]
Just [1,2,3,4,5]
Prelude Control.Monad> sequence [Just 1,Nothing,Just 3,Just 4,Just 5]
Nothing
Run Code Online (Sandbox Code Playgroud)
实质上这只是一个mapM id :: (Monad m, Traversable t) => t (m a) -> m (t a),因为例如对于3个列表,它等于:
-- special case for 3 elements to demonstrate how it works
func3 [a, b, c] = do
ya <- a
yb <- b
yc <- c
return [ya, yb, yc]
Run Code Online (Sandbox Code Playgroud)
或者喜欢:
func3 [a, b, c] = a >>= \ya -> b >>= \yb -> c >>= yc -> return [ya, yb, yc]
Run Code Online (Sandbox Code Playgroud)
(我这里使用了一个特例,因为它mapM引入了一些额外的功能,使其更难以遵循)
因为Maybe,Monad Maybe实现如下:
instance Monad Maybe where
return = Just
Nothing >>= _ = Nothing
(Just x) >>= f = f x
Run Code Online (Sandbox Code Playgroud)
这意味着从其中一个元素(a,b或c)开始Nothing,结果将是Nothing,如果所有值都是Justs,我们将使用lambda表达式"收集"这些元素,并最终生成包含元素的列表.
您可以将列表[]视为a的一般化Maybe(其中Nothing是空列表,并且Just是单个列表),并且可以观察到相同的行为:
Prelude Control.Monad> sequence [[1,2], [4,3]]
[[1,4],[1,3],[2,4],[2,3]]
Prelude Control.Monad> sequence [[1,2], [4,3], []]
[]
Run Code Online (Sandbox Code Playgroud)
这里sequence将生成一个交叉产品,但是如果提供我们应用交叉产品的集合之一的元素的列表之一是空的,那么结果也是空的.