【问题标题】:Why does scala reduce() and reduceLeft() reduce the sequence of the values in the same way?为什么 scala reduce() 和 reduceLeft() 以相同的方式减少值的序列?
【发布时间】:2021-08-18 02:00:51
【问题描述】:

如果我跑:

List(1, 4, 3, 9).reduce {(a, b) => println(s"$a + $b"); a + b}

结果是:

1 + 4
5 + 3
8 + 9

但是,如果我使用reduceLeft 而不是reduce,它也会打印:

1 + 4
5 + 3
8 + 9

我认为reduce 以这种方式减少了值的序列:

(1+4) + (3+9)
(5) + (12)
(17)

reduceLeftreduce 之间的真正区别是什么?

【问题讨论】:

  • “我认为reduce会以这种方式减少值的序列:” ham no. reduce 的大多数实现只是回退到 reduceLeft 但它们可能会回退到 reduceRight - reduce 只是不能保证操作的完成顺序,如果你不关心顺序使用 reduce 因为基础集合将选择最有效的顺序,如果您需要从左到右或从右到左选择其他顺序之一;区别更多的是语义而不是实际的实现细节。如果您想要始终并行搜索的内容,请搜索 aggregate

标签: scala collections


【解决方案1】:

看看这两个函数的类型:

def reduce[B >: A](op: (B, B) => B): B 
def reduceLeft[B >: A](op: (B, A) => B): B

注意,reduceLeftop 的第二个参数是A(与元素类型相同),而在reduce 中是B(与返回值相同)。

这是一个重要的区别。 reduce 中的操作必须是关联的,这意味着你可以将列表拆分成几个段,分别对每个段进行操作,然后再对结果进行组合。

这在你想并行做事时很重要:reduce 可以并行应用到列表的不同部分,然后可以合并结果。

reduceLeft 另一方面,只能按顺序工作(从左到右,顾名思义),操作不必是关联的,并且可以期望它的第二个参数始终是序列的一个元素(并且第一个——到目前为止的操作结果);

考虑

    val sum = (1 to 100).reduce(_ + _) 

这产生与(1 to 100).reduceLeft(_ + _) 相同的结果,除了前者可以并行化。 另一方面:

    val strings = (1 to 100).reduceLeft[Any](_.toString + ";" + _.toString) 

不应该写成reduce(尽管在这个人为的例子中,只要你坚持串行处理,它就可以工作,甚至会工作),因为结果取决于元素被馈送到的顺序操作。

【讨论】:

    【解决方案2】:

    区别在于:

    1. reduce允许以任何顺序减少;对于List,它恰好与reduceLeft 相同,因为它更高效(和it's the default implementation for IterableOnceOps subtypes,所以它们中的大多数都是相同的)。对于另一个集合,它可能相当于reduceRight,对于树类型,它可能更符合您的预期。

    2. 不同的类型签名:

      reduce[B >: A](op: (B, B) => B): B
      

      reduceLeft[B >: A](op: (B, A) => B): B
      

      因为reduceLeftop 的第一个参数始终是调用它的集合的元素,但对于reduce,它可能是递归reduce 调用的结果。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-06-27
      • 1970-01-01
      • 1970-01-01
      • 2018-11-11
      • 2021-10-08
      • 1970-01-01
      • 2013-02-27
      • 1970-01-01
      相关资源
      最近更新 更多