为什么带有 yield 的 for 循环会累积到 Map 而不是 List 中?
Why for-loop with yield accumulates into a Map instead of a List?
我有以下代码:
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
我希望第二条语句生成一个元组列表,但它 return 是一个映射。
我在这里 scala: yield a sequence of tuples instead of map 找到了关于为什么 Map 是 returned 的解释,但我仍在努力寻找一种优雅的方法来 return 元组列表,而不是在这种情况下。
这是因为 for
理解语法如何被编译器转换为一系列方法调用。 map
、flatMap
和 withFilter
是 for
理解排列的目标。这是非常强大和通用的,因为它允许语法适用于任意类型。还有更多,例如 CanBuildFrom
隐式,但本质上将 Map
映射到 Iterable[Tuple[A, B]]
会产生 Map[A, B]
。 The signature is actually overloaded for Map
to provide this behavior
具体来说,给出下面的原始代码
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
翻译大概是这样
val thisIsMap = 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
对 List
的理解将产生 List
.[=36= 来利用混淆原始代码的行为。 ]
但是,请注意将源映射转换为 List
与将生成的映射转换为List
领悟后
如果我们在源 (dummy
) 上调用 toList
,我们会得到 List((1,2), (1,4), (6,7))
,而如果我们在结果上调用它,我们会得到 List((1,4), (6,7))
,这是不言而喻的所以谨慎选择的原因。
尝试
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)
完成答案后,post 将在此处对我自己的问题进行 TLDR 摘要。
由 for 理解循环 return 返回的数据结构类型预计与 for 循环开始迭代。
IE。如果它开始遍历 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,但 return 是一个列表。这是因为 yield 不是 return 可以转换为 Map 的类型。但是它可以转换成List,所以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) 覆盖。 IE。该地图仅包含 2 个元素。
我有以下代码:
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
我希望第二条语句生成一个元组列表,但它 return 是一个映射。 我在这里 scala: yield a sequence of tuples instead of map 找到了关于为什么 Map 是 returned 的解释,但我仍在努力寻找一种优雅的方法来 return 元组列表,而不是在这种情况下。
这是因为 for
理解语法如何被编译器转换为一系列方法调用。 map
、flatMap
和 withFilter
是 for
理解排列的目标。这是非常强大和通用的,因为它允许语法适用于任意类型。还有更多,例如 CanBuildFrom
隐式,但本质上将 Map
映射到 Iterable[Tuple[A, B]]
会产生 Map[A, B]
。 The signature is actually overloaded for Map
to provide this behavior
具体来说,给出下面的原始代码
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
翻译大概是这样
val thisIsMap = 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
对 List
的理解将产生 List
.[=36= 来利用混淆原始代码的行为。 ]
但是,请注意将源映射转换为 List
与将生成的映射转换为List
领悟后
如果我们在源 (dummy
) 上调用 toList
,我们会得到 List((1,2), (1,4), (6,7))
,而如果我们在结果上调用它,我们会得到 List((1,4), (6,7))
,这是不言而喻的所以谨慎选择的原因。
尝试
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)
完成答案后,post 将在此处对我自己的问题进行 TLDR 摘要。
由 for 理解循环 return 返回的数据结构类型预计与 for 循环开始迭代。 IE。如果它开始遍历 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,但 return 是一个列表。这是因为 yield 不是 return 可以转换为 Map 的类型。但是它可以转换成List,所以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) 覆盖。 IE。该地图仅包含 2 个元素。