concatMap
是什么?
我知道concat
和map
分别是什么。它们只是简单地合在一起吗,还是完全不同的函数?
是的,concatMap
函数就是将concat
和map
组合在一起而已。因此得名。简单地把函数组合在一起意味着将它们合并:
(.) :: (b -> c) -> (a -> b) -> a -> c
然而,因为map
的类型签名,仅仅使用函数组合就无法将concat
和map
结合在一起。
map :: (a -> b) -> [a] -> [b]
-------- --- ---
a b c
正如您所看到的,函数组合期望一个类型为a -> b
的函数,但map
的类型是a -> b -> c
。要将concat
与map
组合,您需要使用.:
操作符:
(.:) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
concat
函数的类型标记为:
concat :: [[a]] -> [a]
----- ---
c d
因此,concat .: map
的类型为:concat .: map :: (a -> [b]) -> [a] -> [b]
---------- --- ---
a b d
这与 concatMap
的作用相同:
concatMap :: (a -> [b]) -> [a] -> [b]
.:
运算符本身可以用函数组合的方式来书写:
(.:) = (.) (.) (.)
-- or
(.:) = (.) . (.)
因此,concatMap
可以这样写:concatMap = (.) (.) (.) concat map
-- or
concatMap = (concat .) . map
-- or
concatMap = concat .: map
如果您将concatMap
的参数交换位置,则会得到列表单子的>>=
(绑定)函数:
instance Monad [] where
return x = [x]
(>>=) = flip concatMap
fail _ = []
flip concatMap :: [a] -> (a -> [b]) -> [b]
>>= :: Monad m => m a -> (a -> m b) -> m b
这使得它与列表单子的=<<
函数相同:
concatMap :: (a -> [b]) -> [a] -> [b]
=<< :: Monad m => (a -> m b) -> m a -> m b
现在你已经了解了关于concatMap
的所有内容。它只是对map
结果应用concat
而已。因此得名。
从概念上讲是的,但是 实际实现 是不同的:
concatMap :: (a -> [b]) -> [a] -> [b]
concatMap f = foldr ((++) . f) []
concatMap
为什么要使用foldr
实现可能有所帮助,原因是利用foldr/build
技术进行短路除树操作:https://ghc.haskell.org/trac/ghc/wiki/FoldrBuildNotes - Aadit M Shah查看文档可以得知:
concatMap :: (a -> [b]) -> [a] -> [b]
对一个列表应用一个函数并将结果连接起来
其定义如下:
-- | Map a function over a list and concatenate the results. concatMap :: (a -> [b]) -> [a] -> [b] concatMap f = foldr ((++) . f) []
比较以下是在ghci中得到的输出:
*Main> concatMap (++"! ") ["one", "two", "three"] "one! two! three! " *Main> concat $ map (++"! ") ["one", "two", "three"] "one! two! three! "
>>=
的列表实现,只是名字不同而已。 :) - porges
concatMap f = concat . map f
是无法区分的。我不确定它在理论上是否更有效率,但我认为这只是一种方便的组合。 - luquiconcatMap
是用foldr
实现的,所以可以使用foldr/build
技术进行优化。因此,它确实更加高效:https://ghc.haskell.org/trac/ghc/wiki/FoldrBuildNotes - Aadit M Shah