Map展开和flatMap不等价

14

我原以为在Scala中,使用map(f).flatten的构造方式等同于flatMap(f)。但是通过这个例子,情况并非如此。我想知道这个case类的作用是什么。如果我使用整数,这两种写法是等价的。但在我的情况下,它们不等价。

case class CTest(v: Int)
val s = Set(Map(CTest(0) -> List(0, 3), CTest(1) -> List(0, 2)))
val possibilities = s flatMap { m =>
  val mapping = m flatMap {
    case (label, destNodes) => destNodes map {
      case nodes => (label, nodes) }
  }
  mapping
}
possibilities

产量

Set((CTest(0),3), (CTest(1), 2))

case class CTest(v: Int)
val s = Set(Map(CTest(0) -> List(0, 3), CTest(1) -> List(0, 2)))
val possibilities = s flatMap { m =>
  val mapping = m map {
    case (label, destNodes) => destNodes map {
      case nodes => (label, nodes) }
  }
  mapping.flatten
}
possibilities
产量。
Set((CTest(0),0), (CTest(0),3), (CTest(1),0), (CTest(1),2))
任何想法为什么?
2个回答

10

这是由于中间数据结构的存在而发生的。

我会采用你的例子的简化版本。

val m = Map(CTest(0) -> List(0, 3), CTest(1) -> List(0, 2))

使用 flatMap 时,您直接创建一个 Map[CTest, Int]

scala> m flatMap {
 |     case (label, destNodes) => destNodes map {
 |       case nodes => (label, nodes) }
 |   }
res3: scala.collection.immutable.Map[CTest,Int] = Map(CTest(0) -> 3, CTest(1) -> 2)

由于Map的键的唯一性,(CTest(0),0)(CTest(1),0)将从结果中删除。当你对集合执行flatMap时,你会得到一个TuplesSet,它们位于Map中。

在您的第二个示例中,您进行映射和展平操作。

val mapping = m map {
 |     case (label, destNodes) => destNodes map {
 |       case nodes => (label, nodes) }
 |   }
mapping: scala.collection.immutable.Iterable[List[(CTest, Int)]] = List(List((CTest(0),0), (CTest(0),3)), List((CTest(1),0), (CTest(1),2)))

mapping.flatten
res4: scala.collection.immutable.Iterable[(CTest, Int)] = List((CTest(0),0), (CTest(0),3), (CTest(1),0), (CTest(1),2))

在这个过程中没有创建任何Map或其他保留唯一性的数据结构,因此值不会被删除。


7

让我们看一下flatMap的实现:

def flatMap[B, That](f: A => GenTraversableOnce[B])(implicit bf: CanBuildFrom[Repr, B, That]): That = {
  def builder = bf(repr) // extracted to keep method size under 35 bytes, so that it can be JIT-inlined
  val b = builder
  for (x <- this) b ++= f(x).seq
  b.result
}
flatMap 的结果取决于原始集合类型和函数 f 的结果类型。在第一个示例中,您可以从 map 生成元组序列,这样编译器就会选择像 CanBuildFrom [Map [A,B],(C,D),Map [C,D]] 这样的实现,它为 Map 提供了构建程序,导致同一键被覆盖。
您可以直接将 map 转换为普通 iterable,这将产生您想要的结果:
case class CTest(v: Int)
val s = Set(Map(CTest(0) -> List(0, 3), CTest(1) -> List(0, 2)))
val possibilities = s flatMap { m =>
  val mapping = m.toIterable.flatMap {
    case (label, destNodes) => destNodes map {
      case nodes => (label, nodes) }
  }
  mapping
}
possibilities

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