concatMap是什么?

29

concatMap是什么? 我知道concatmap分别是什么。它们只是简单地合在一起吗,还是完全不同的函数?


8
行为上,concatMap f = concat . map f 是无法区分的。我不确定它在理论上是否更有效率,但我认为这只是一种方便的组合。 - luqui
6
因为concatMap是用foldr实现的,所以可以使用foldr/build技术进行优化。因此,它确实更加高效:https://ghc.haskell.org/trac/ghc/wiki/FoldrBuildNotes - Aadit M Shah
3个回答

35

是的,concatMap函数就是将concatmap组合在一起而已。因此得名。简单地把函数组合在一起意味着将它们合并:

(.) :: (b -> c) -> (a -> b) -> a -> c

然而,因为map的类型签名,仅仅使用函数组合就无法将concatmap结合在一起。

map :: (a -> b) -> [a] -> [b]
       --------    ---    ---
          a         b      c

正如您所看到的,函数组合期望一个类型为a -> b的函数,但map的类型是a -> b -> c。要将concatmap组合,您需要使用.:操作符:

(.:) :: (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而已。因此得名。


非常好的回答。只是一个打字错误:“In you flip [...]” 应该是 If - YoTengoUnLCD

17

从概念上讲是的,但是 实际实现 是不同的:

concatMap :: (a -> [b]) -> [a] -> [b]
concatMap f =  foldr ((++) . f) []

4
concatMap为什么要使用foldr实现可能有所帮助,原因是利用foldr/build技术进行短路除树操作:https://ghc.haskell.org/trac/ghc/wiki/FoldrBuildNotes - Aadit M Shah

11

查看文档可以得知:

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! "

4
如果你想知道为什么它存在,我认为这是因为它是>>=的列表实现,只是名字不同而已。 :) - porges

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接