【问题标题】:Scala - Unexpected MapReduce Behavior - square of Even numbersScala - 意外的 MapReduce 行为 - 偶数的平方
【发布时间】:2018-06-17 15:42:09
【问题描述】:

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

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

【问题讨论】:

  • 是什么让您期望 total 在第一次调用时为 0?你没有将0 传递给reduce 任何地方......它怎么可能猜到你的意思?
  • 我做了一个假设,它会初始化为零。似乎是错误的假设

标签: java scala mapreduce scala-collections


【解决方案1】:

在第一次调用传递给.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

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

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

【讨论】:

  • 我不知道你所说的“你不对前者求平方,只有后者,所以你的结果是 2”我想对过滤后的偶数进行平方 - 2,4 和然后将它们添加到总变量 1: (total = 0, elem = 2) => 0 + (2 * 2) = 4 2. (total = 4, elem = 4) => 4 + (4 *4) = 20这不是预期的行为吗?
  • 嗯,第一次通话时不是(total = 0, elem = 2),而是(total = 2, elem = 4) - 这就是问题所在。
  • 我明白了,你说的是reduce函数使用第一个元素,即2作为初始总值,然后应用该函数。如此有效,只完成了一次通过,即:reduce((2,4) => 2 + 4*4) = 18。感谢您的输入!
【解决方案2】:

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

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

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

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

【讨论】:

  • 很好奇,为什么平方不适用于第一个元素?这是设计使然吗?
  • @aces。这正是reduce 的工作方式。您没有像在 foldLeft/foldRight 中那样提供初始元素,它需要从某个地方开始,以便它抓取集合中的第一个元素并以此开始。
猜你喜欢
  • 2013-04-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-23
  • 2015-03-24
  • 2013-01-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多