让我们首先定义如何对单个操作执行操作Maybe
,然后我们将其扩展到整个列表。
mapMaybe :: (a -> b) -> Maybe a -> Maybe b
mapMaybe f Nothing = Nothing
mapMaybe f (Just x) = Just (f x)
If the Maybe
包含一个值,mapMaybe
适用f
如果它不包含值,那么我们只返回一个空值Maybe
.
但我们有一个list of Maybe
s,所以我们需要申请mapMaybe
给他们每个人。
mapMaybes :: (a -> b) -> [Maybe a] -> [Maybe b]
mapMaybes f ms = [mapMaybe f m | m <- ms]
这里我使用列表理解来评估mapMaybe f m
对于每个m
in ms
.
现在介绍更先进的技术。将函数应用于容器中的每个值的模式由Functor
类型类。
class Functor f where
fmap :: (a -> b) -> f a -> f b
A type f
is a Functor
如果你可以编写一个函数来获取函数a
to b
,并将该函数应用于f
充满a
得到一个f
充满b
s。例如,[]
and Maybe
都是Functor
s:
instance Functor Maybe where
fmap f Nothing = Nothing
fmap f (Just x) = Just (f x)
instance Functor [] where
fmap f xs = [f x | x <- xs]
Maybe
的版本fmap
与mapMaybe
我在上面写了,并且[]
的实现使用列表理解来应用f
到列表中的每个元素。
现在,要写mapMaybes :: (a -> b) -> [Maybe a] -> [Maybe b]
,您需要使用以下命令对列表中的每个项目进行操作[]
的版本fmap
,然后对个体进行操作Maybe
s using Maybe
的版本fmap
.
mapMaybes :: (a -> b) -> [Maybe a] -> [Maybe b]
mapMaybes f ms = fmap (fmap f) ms
-- or:
mapMaybes = fmap . fmap
请注意,我们实际上调用了两个不同的fmap
这里的实现。外面的那张是fmap :: (Maybe a -> Maybe b) -> [Maybe a] -> [Maybe b]
,它发送到[]
's Functor
实例。里面的一个是(a -> b) -> Maybe a -> Maybe b
.
还有一个附录 - 尽管这相当深奥,所以如果您不理解这里的所有内容,请不要担心。我只是想让你尝尝我认为很酷的东西。
这条“链fmap
s”风格(fmap . fmap . fmap ...
)是深入研究多层结构的一个非常常见的技巧。每个fmap
有一种类型(a -> b) -> (f a -> f b)
,所以当你用(.)
您正在构建一个高阶函数。
fmap :: Functor g => (f a -> f b) -> (g (f a) -> g (f b))
fmap :: Functor f => (a -> b) -> (f a -> f b)
-- so...
fmap . fmap :: (Functor f, Functor g) => (a -> b) -> g (f a) -> g (f b)
所以如果你有一个函子的函子(函子的函子......),那么n fmap
s 可以让您映射级别的元素n的结构。康纳尔·埃利奥特称这种风格为“语义编辑器组合器” http://conal.net/blog/posts/semantic-editor-combinators.
该技巧也适用于traverse :: (Traversable t, Applicative f) => (a -> f b) -> (t a -> f (t b))
,这是一种“有效的fmap
".
traverse :: (...) => (t a -> f (t b)) -> (s (t a) -> f (s (t b)))
traverse :: (...) => (a -> f b) -> (t a -> f (t b))
-- so...
traverse . traverse :: (...) => (a -> f b) -> s (t a) -> f (s (t b))
(我省略了前面的位=>
因为我用完了水平空间。)所以如果你有一个可遍历的可遍历(可遍历的......),你可以对水平上的元素执行有效的计算n只是通过写作traverse
n次。像这样编写遍历是背后的基本思想lens
图书馆。