Scala - 意外的 MapReduce 行为 - 偶数的平方

Scala - Unexpected MapReduce Behavior - square of Even numbers

我正在学习 Scala,对一个 简单问题的输出感到困惑 计算范围 .

之间偶数的平方和

代码段 1:

val rangeOfNumbers = (1 to 5)
val listOfNumbers = rangeOfNumbers.toList
val squaresOfEvenNumbers = listOfNumbers
     .filter(element => element % 2 == 0)
     .reduce((total, element) => total+ (element * element))

println(squaresOfEvenNumbers)
Outputs: 18 -> which is clearly wrong

**EDIT** - Simulation:

I want to square the filtered even numbers - 2,4 and then add them to total variable

1: (total = 0, elem = 2) => 0 + (2 * 2) = 4
2. (total = 4, elem = 4) => 4 + (4 *4 ) = 20

Isn't this the expected behavior?

代码段 2:

val rangeOfNumbers = (1 to 5)
val listOfNumbers = rangeOfNumbers.toList
val filteredEvenNumberSquaredList = listOfNumbers
    .filter(element => element % 2 == 0)
    .map(evenEle => evenEle * evenEle)
    .reduce((total, square) => total + square)

println(filteredEvenNumberSquaredList)
Outputs: 20 -> which is correct

你能解释一下第一种方法有什么问题吗?内部发生了什么?

当您使用 reduce 时,加倍不会应用于第一个元素:

List(2, 4).reduce((sum, el) => sum + el * el)
=> 2 + 4 * 4
=> 18

reduce(对于非空列表)应满足:

l.reduce(op) == l.tail.foldLeft(l.head)(op)

在第一次调用传递给 .reduce 的 lambda 时,total 是列表的第一个元素,element 是第二个元素。您不对前者求平方,只对后者求平方,所以您的结果相差 2。

您可能要考虑使用 .foldLeft 而不是 reduce

  list.foldLeft(0) { case (sum, elem) => sum + elem*elem }

它与 .reduce 相同,只是它以您提供的值开始(在本例中为 0),并且对列表的每个元素都以相同的方式调用,包括第一个(它也适用于空列表,而 .reduce 会崩溃)。

或者,只是 list.map(x => x*x).sum

顺便说一句,您可以在范围本身上执行所有 filtering/mapping 等操作 - 无需先将其转换为列表:

 val squaresOfEvenNumbers = (1 to 5)
   .collect { case x if x % 2 == 0 => x*x }
   .sum