Scala:带有元组的flatMap

时间:2016-08-12 18:38:03

标签: scala flatmap

为什么以下声明对.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))

2 个答案:

答案 0 :(得分:4)

假设input List[Int]类型,mapIntA采用函数,而flatMapInt获取函数{1}}至List[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))

答案 1 :(得分:3)

要了解这是如何工作的,明确解压缩您发送给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函数知道如何对单个元素执行操作。