【问题标题】:Map flatten and flatmap not equivalentMap flatten 和 flatmap 不等价
【发布时间】:2013-11-26 11:13:26
【问题描述】:

我以为Scala construct map(f).flatten was equivalent to flatMap(f)。但在这个例子中,情况并非如此。我想知道案例类在其中的作用是什么。如果我使用整数,两者都是等价的。但就我而言,我不能。

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))

知道为什么吗?

【问题讨论】:

    标签: scala dictionary case-class


    【解决方案1】:

    这是由于中间数据结构造成的。

    我将采用您的示例的简单版本。

    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 设置过度时,您将获得Tuples 中的Set,它们位于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 或其他保留唯一性的数据结构。所以值不会被丢弃。

    【讨论】:

      【解决方案2】:

      看看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 转换为纯可迭代的,这将产生您想要的结果:

      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
      

      【讨论】:

        猜你喜欢
        • 2018-08-17
        • 1970-01-01
        • 1970-01-01
        • 2017-02-02
        • 2015-01-16
        • 1970-01-01
        • 1970-01-01
        • 2011-09-01
        • 1970-01-01
        相关资源
        最近更新 更多