【问题标题】:Scala: flatMap with tuplesScala:带有元组的 flatMap
【发布时间】:2023-03-22 05:16:01
【问题描述】:

为什么下面的语句对.map() 有效,而对.flatMap() 无效?

 val tupled = input.map(x => (x*2, x*3))

 //Compilation error: cannot resolve reference flatMap with such signature
 val tupled = input.flatMap(x => (x*2, x*3))

不过,这句话没有问题:

val tupled = input.flatMap(x => List(x*2, x*3))

【问题讨论】:

  • input 的类型是什么?检查 mapflatMap 方法签名。它有不同的参数,这就是你的代码有编译错误的原因。
  • 因为flatMap用于扁平化结构。当你得到 M[M[A]]map 时,你会得到 M[A]flatMap
  • 嵌套的M 有时可能不同,N.. 例如List[Option[String]] => List[String]
  • 事情是a.flatMap(a => b)可以大致近似为a.map(a => b).flatten。基本上要让flatMap 工作,函数的返回类型应该是一个单子或者你可以展平的东西。

标签: scala flatmap


【解决方案1】:

假设input 的类型为List[Int]map 采用从IntA 的函数,而flatMap 采用从IntList[A] 的函数。

根据您的用例,您可以选择其中一个,但它们绝对不能互换。

例如,如果您只是转换 List 的元素,您通常希望使用 map

List(1, 2, 3).map(x => x * 2) // List(2, 4, 6)

但您想更改 List 的结构,并且 - 例如 - 将每个元素“分解”到另一个列表中,然后将它们展平,flatMap 是您的朋友:

List(1, 2, 3).flatMap(x => List.fill(x)(x)) // List(1, 2, 2, 3, 3, 3)

如果使用map,您将使用List(List(1), List(2, 2), List(3, 3, 3))

【讨论】:

    【解决方案2】:

    要了解其工作原理,明确解压缩您发送到 mapflatMap 的函数并检查它们的签名会很有用。我在这里重写了它们,所以你可以看到f 是从Int(Int, Int) 元组的函数映射,g 是从IntList[Int] 的映射函数。

    val f: (Int) => (Int, Int) = x => (x*2, x*3)
    val g: (Int) => List[Int] = x => List(x*2, x*3)
    
    List(1,2,3).map(f)
    //res0: List[(Int, Int)] = List((2,3), (4,6), (6,9))
    List(1,2,3).map(g)
    //res1: List[List[Int]] = List(List(2, 3), List(4, 6), List(6, 9))
    //List(1,2,3).flatMap(f)  // This won't compile
    List(1,2,3).flatMap(g)
    //res2: List[Int] = List(2, 3, 4, 6, 6, 9)
    

    那么为什么flatMap(f) 不编译呢?让我们看一下flatMap 的签名,在这种情况下是从List 实现中提取的:

    final override def flatMap[B, That](f : scala.Function1[A, scala.collection.GenTraversableOnce[B]])(...)
    

    这有点难解压,我省略了一些,但关键是GenTraversableOnce 类型。 List,如果您遵循它的继承链,则将其作为构建它的特征,因此具有从某种类型 A 映射到 List 的函数(或具有 GenTraversableOnce 特征的任何对象) 将是一个有效的函数。值得注意的是,元组没有这个特征。

    这就是为什么键入错误的根本解释,并且值得解释,因为任何说“无法解析具有此类签名的引用”的错误都意味着它找不到采用显式类型的函数你提供。在 Scala 中,类型经常被推断出来,因此您可以很好地确保您提供的类型是您正在调用的方法所期望的类型。

    请注意,flatMap 在函数式编程中具有标准含义,粗略地说,任何使用单个元素并产生 n 个元素的映射函数,但您的最终结果是所有这些列表的串联。因此,您传递给flatMap 的函数将始终期望生成一个列表,并且不期望flatMap 函数知道如何作用于单个元素。

    【讨论】:

      猜你喜欢
      • 2019-03-18
      • 2018-09-05
      • 2020-04-05
      • 1970-01-01
      • 2012-08-27
      • 2020-08-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多