我正在使用这段代码来获取元素映射的加权和:
val wSum = elements.map(element=>
weights(element) * values(element))
.sum
在测试时,如果我通过
weights = Map(1 -> 1.0, 2 -> 1.0, 3 -> 1.0)
values = Map(1 -> 0.2, 2 -> 0.6, 3 -> 0.4)
它按预期返回1.2。另一方面,如果我通过
weights = Map(1 -> 1.0, 2 -> 1.0, 3 -> 1.0)
values = Map(1 -> 0.5, 2 -> 0.5, 3 -> 0.5)
这将返回0.5。这似乎很危险,我是否错过了常用的替代方法?
答案 0 :(得分:4)
代码的行为正确,所以这并不是真的错误,当然也没有危险。
如果您实际上是尝试为每个元素计算weights*values
,然后对结果求和,则有两种常见的方法:
最简单的就是
elements.toList.map(element=>
weights(element) * values(element))
.sum
另一种选择是使用foldLeft
一次计算总和:
elements.foldLeft(0.0){
case (prev, element) => prev + weights(element) * values(element)
}
要了解原始代码中的行为,您需要了解map
的作用:
集合上的
map
方法通过将函数应用于旧集合的每个元素并将其添加到新集合中,来创建相同类型的新集合。
关键点在于集合类型没有更改,因此适用于旧集合的任何规则也适用于新集合。如果对集合进行了订购,则保留订购。如果集合是自排序的,则将对新集合进行排序。如果该集合具有类似集合的属性,则不会将重复的元素添加到新集合中。
在此问题中,通过在Scala代码中使用Java类使问题变得更加复杂。 Java集合的行为可能与Scala行为不同,因此正确的方法是使用asScala
中的JavaConverters
将所有Java集合转换为Scala集合。
答案 1 :(得分:1)
因此在集合上映射会返回一个集合。对我来说似乎合乎逻辑。 IIRC保留原始集合的返回类型是scala集合库的重要组成部分。您的直接解决方法是在映射之前调用elements.toSeq
。
但是,我将使用一个case类来封装Elements:
case class Element(weight: Double, value: Double) {
def weightedValue = weight * value
}
val elements = Map(1 -> Element(1.0, 0.5), 2 -> Element(1.0, 0.5))
elements.values.map(_.weightedValue).sum
答案 2 :(得分:0)
Set.map()调用产生的Set折叠了重复的值,就像Set应该做的一样。