为什么带有收益的for循环累积到Map中而不​​是List中?

时间:2019-07-10 22:24:20

标签: scala scala-collections for-comprehension

我有以下代码:

val dummy = Map(1 -> Map(2 -> 3.0,
                         4 -> 5.0),
                6 -> Map(7 -> 8.0))

val thisIsList = for (x <- dummy; y <- x._2.keys) yield s"(${x._1}, ${y})"
println(thisIsList)  // List((1, 2), (1, 4), (6, 7))

val thisIsMap = for (x <- dummy; y <- x._2.keys) yield new Tuple2(x._1, y)
println(thisIsMap)   // Map(1 -> 4, 6 -> 7) - this is not what I want    

我希望第二条语句产生一个元组列表,但它返回一个Map。 我在scala: yield a sequence of tuples instead of map处找到了有关为什么返回Map的解释,但在这种情况下,我仍然在努力寻找一种优雅的方法来返回元组列表。

3 个答案:

答案 0 :(得分:7)

这是因为编译器如何将for理解语法转换为一系列方法调用。 map理解的排列以flatMapwithFilterfor为目标。这是非常强大且通用的,因为它允许语法与任意类型一起使用。除此之外,还有其他含义,例如隐式CanBuildFrom,但实际上将Map映射到Iterable[Tuple[A, B]]会产生Map[A, B]The signature is actually overloaded for Map to provide this behavior

翻译大致像这样

val thisIsMap1 = dummy.flatMap { x =>
  x._2.keys.map { y =>
    (x._1, y)
  }
}

查看此fiddle

为了获得所需的列表,我们可以编写

val thisIsMap = (for (x <- dummy; y <- x._2.keys) yield (x._1, y)).toList

但是,如果考虑一下我们对for理解的了解,我们可以将其写得更加优雅

val thisIsMap = for (x <- dummy.toList; y <- x._2.keys) yield (x._1, y)

在上文中,我们通过推断对for的{​​{1}}理解将产生List来利用混淆了原始代码的行为。

但是 ,请注意,在理解后,将源转换为List与将结果映射转换为List之间的区别

如果在源(List上调用toList,则得到dummy;而如果在结果上调用它,则得到List((1,2), (1,4), (6,7)),这是显而易见的原因,因此仔细选择。

答案 1 :(得分:2)

尝试

dummy
  .view
  .mapValues(_.keys.toList)
  .flatMap { case (key: Int, values: List[Int]) => values.map((key, _)) }
  .toList

输出

res0: List[(Int, Int)] = List((1,2), (1,4), (6,7)

答案 2 :(得分:0)

在回答了所有问题之后,将在此处发布我自己的问题的TLDR摘要。

for 理解循环返回的数据结构的类型应该与 for 循环开始进行迭代的类型相同。 即如果开始在Map上进行迭代-希望Map成为最终结果。

val thisIsList = for (x <- dummy; y <- x._2.keys) yield s"(${x._1}, ${y})"
println(thisIsList)  // List((1, 2), (1, 4), (6, 7))

在这个问题示例中, for 循环开始在Map上进行迭代,但返回一个List。发生这种情况是因为yield不会返回可转换为Map的类型。但是它可以转换为列表,因此Scala可以做到。

val thisIsMap = for (x <- dummy; y <- x._2.keys) yield new Tuple2(x._1, y)
println(thisIsMap)   // Map(1 -> 4, 6 -> 7) - this is not what I want 

尽管在此示例中,一切都按预期进行,但是由于生成的Map不能具有重复的键,因此元组(1,2)被元组(1,4)覆盖。即该地图仅包含2个元素。