Haskell中的fmap和"flat map"

tra*_*ans 14 haskell

所有这一次,当任何Haskell演讲中谈到"平面地图"时,通常与Monads有关,我认为它被称为"平面",原因是它平坦了容器.所以

[[1,2],[3,4]]
Run Code Online (Sandbox Code Playgroud)

将被处理就像它一样

[1,2,3,4]
Run Code Online (Sandbox Code Playgroud)

但是现在我发现fmap和map基本上是一回事,唯一的区别是一个用于仿函数而另一个用于just列表.最后,这只是在使用map时避免混淆错误消息.

真的吗?如果是这样,为什么f在fmap中意味着"平坦",为什么不"functor map"?

Ale*_*ing 30

如果是的话,为什么ffmap来意味"扁平化",为什么不"仿函数图"?

你的直觉是正确的:ffmap 主张"函子图",而不是"平面地图"在所有.实际上,在较新的类似语言中,例如PureScript,名称就是这样map.然而,Haskell map首先是为列表定义的,因此提出一个新名称很困难.使用Functor中的F是一个简单的(如果不是特别有创意的话)选择.

讲师更有可能是指monadic bind函数,>>=.由于x >>= f等价join (fmap f x),有时也会flatMap在其他语言中调用bind .它具有您在列表中所期望的行为,例如:

> [1,2,3] >>= \x -> [x,x]
[1,1,2,2,3,3]
Run Code Online (Sandbox Code Playgroud)

但是,请记住,这个"平面地图"不会递归地变平到任意深度,这一点很重要.实际上,如果没有复杂的类型类技巧,在Haskell中编写这样的函数实际上是不可能的.自己尝试一下:flatten函数的类型签名是什么样的,即使是直接在列表上运行的签名?

flatten :: ??? -> [a]
Run Code Online (Sandbox Code Playgroud)

>>=相比之下,该函数非常简单:它就像是fmap,但每个输出元素必须包含在仿函数中,并将>>=结果浅"扁平化"为单个包装器.这个操作是monad的本质,这就是为什么>>=函数存在于Monad类型类中,但是fmapFunctor.

这个答案来自原始问题的一些评论,因此我将其标记为社区维基.欢迎编辑和改进.


div*_*ero 10

以下是一些明确的等效示例,说明如何flatMap在 Haskell 中执行操作。

Prelude> map (replicate 3) [1..4]
[[1,1,1],[2,2,2],[3,3,3],[4,4,4]]
Prelude> fmap (replicate 3) [1..4]
[[1,1,1],[2,2,2],[3,3,3],[4,4,4]]
Prelude> concat [[1,2],[3,4]]
[1,2,3,4]
Prelude> concat (map (replicate 3) [1..4])
[1,1,1,2,2,2,3,3,3,4,4,4]
Prelude> concat $ map (replicate 3) [1..4]
[1,1,1,2,2,2,3,3,3,4,4,4]
Prelude> concatMap (replicate 3) [1..4]
[1,1,1,2,2,2,3,3,3,4,4,4]
Prelude> replicate 3 `concatMap` [1..4]
[1,1,1,2,2,2,3,3,3,4,4,4]
Prelude> [1..4] >>= replicate 3
[1,1,1,2,2,2,3,3,3,4,4,4]
Run Code Online (Sandbox Code Playgroud)

应该清楚的flatMap是,先映射然后扁平化,你扁平化地图的输出,而不是扁平化你将要处理的输入列表(这不是flatMap,这没有名字,它只是一个扁平化然后地图)。