【问题标题】:fmap and "flat map" in HaskellHaskell 中的 fmap 和“平面地图”
【发布时间】:2017-02-22 19:58:55
【问题描述】:

一直以来,当任何 Haskell 讲座谈到“平面地图”时,通常与 Monad 相关,我认为它被称为“平面”是有原因的,即它会使容器变平。所以

[[1,2],[3,4]]

会像处理它一样处理

[1,2,3,4]

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

这是真的吗?如果是这样,为什么 fmap 中的 f 表示“平面”,为什么不是“函子映射”?

【问题讨论】:

  • f 中的 fmap 并不意味着 flat。 Haskell 中flatMap 的等价物是(>>=)。列表的 map 函数是首先定义的,因此更通用的 fmap 函数需要另一个名称。
  • 相反,fmapmap 对列表函子以外的其他函子的推广。谁告诉你fmapflat map 的缩写是错误的。
  • 你确定你提到的讲座在说“平面地图”时指的是fmap吗?如果它指的是concatMap(又名>>=)会更有意义,在其他语言中通常称为flatMap,并且其行为方式符合您的预期。
  • @trans [1,2,3] >>= \x -> [x,x]
  • 展平是最后执行的,而不是第一个。在[[1,2],[3,4]] >>= \x -> [x]中,我们获取列表的每个元素并将其绑定到x(因此x=[1,2]x=[3,4]),然后我们将函数\x->[x]应用于每个x,得到结果列表[ [[1,2]], [[3,4]] ] .最后,我们将最后一个列表展平为结果:[[1,2],[3,4]]

标签: haskell


【解决方案1】:

这里有一些明确的等效示例,说明如何在 Haskell 中执行 flatMap

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]

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

【讨论】:

    【解决方案2】:

    如果是这样,为什么fmap 中的f 表示“平面”,为什么不表示“函子映射”?

    你的直觉是对的:fmap 中的f 确实代表“函子映射”,而不是“平面映射”。事实上,在较新的类似语言中,例如 PureScript,名称只是 map。不过,Haskell map 首先是为列表定义的,因此很难想出一个新名称。使用 Functor 的 F 是一个简单的选择,如果不是特别有创意的话。

    讲师更可能指的是一元绑定函数>>=。由于x >>= fjoin (fmap f x) 等价,bind 在其他语言中有时也称为flatMap。它具有您在列表中所期望的行为,例如:

    > [1,2,3] >>= \x -> [x,x]
    [1,1,2,2,3,3]
    

    但重要的是要记住,这个“平面地图”不会递归地展平到任意深度。事实上,如果没有一些复杂的类型类技巧,在 Haskell 中编写这样的函数是不可能的。自己尝试一下:flatten 函数的类型签名会是什么样子,即使是直接对列表进行操作的函数?

    flatten :: ??? -> [a]
    

    相比之下,>>= 函数非常简单:它类似于 fmap,但每个输出元素都必须包装在函子中,而 >>= 将结果浅层“扁平化”到单个包装器中。这个操作是 monad 的本质,这就是为什么>>= 函数存在于Monad 类型类中,而fmap 存在于Functor 中。

    这个答案取自原始问题上的一些 cmets,所以我将其标记为社区 wiki。欢迎编辑和改进。

    【讨论】:

    • 这不仅仅是数字,即使是不同深度的列表,[[[1,2]],[3],[4]] >>= \x -> x 只会产生:: (Num [t], Num t) => [[t]],而不是[[1,2],3,4]
    • @trans 请注意,[[[1,2]],[3],[4]] 不是有效的 Haskell 值。它没有类型——既不是Num t => [[t]]也不是Num t => [[[t]]],因为深度不一致。
    • 获得该类型的原因是因为Num 的工作方式,因此它推断出Num t => [[t]] 的类型,它期望[1, 2] 有一个Num 实例。当然,事实并非如此。尝试使用非数字以获得更清晰的结果。
    • “如果没有一些复杂的类型类技巧,在 Haskell 中编写这样的函数是不可能的” - 我不同意。 data Nested f a = Flat a | Nested (f (Nested f a)) deriving Foldable 是一个非常简单的类型,可以任意嵌套Foldables
    • @BenjaminHodgson 我的意思是只使用内置的 [] 类型。如果您使用自己的包装器,这显然很容易。
    猜你喜欢
    • 2015-12-24
    • 1970-01-01
    • 2011-02-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多