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

时间:2018-01-08 16:14:31

标签: java scala mapreduce scala-collections

我正在学习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

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

2 个答案:

答案 0 :(得分:3)

在第一次调用lambda时,传递给.reducetotal是列表的第一个元素,element是第二个元素。你不会对前者产生影响,只有后者,所以你的结果是2。

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

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

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

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

顺便说一句,你可以对范围本身进行所有过滤/映射等 - 无需先将其转换为列表:

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

答案 1 :(得分:1)

当您使用List(2, 4).reduce((sum, el) => sum + el * el) => 2 + 4 * 4 => 18 时,加倍并不适用于第一个元素:

reduce

l.reduce(op) == l.tail.foldLeft(l.head)(op) (对于非空列表)应满足:

tf.map_fn