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