Scala 聚合泛化折叠和折叠泛化如何减少?

Scala 聚合泛化折叠和折叠泛化如何减少?,scala,apache-spark,Scala,Apache Spark,据我所知,aggregate是fold的泛化,而fold又是reduce的泛化 类似地,combineByKey是aggregateebykey的泛化,后者又是foldByKey的泛化,后者又是reduceByKey的泛化 然而,我很难为这七种方法中的每一种找到简单的例子,而这七种方法只能用它们来表达,而不能用它们不那么一般的版本来表达。例如,我发现为fold提供了一个示例,但我也能够在相同的情况下使用reduce 到目前为止,我发现: 我读到更一般化的方法可能更有效,但这将是一个非功能性要求

据我所知,
aggregate
fold
的泛化,而fold又是
reduce
的泛化

类似地,
combineByKey
aggregateebykey
的泛化,后者又是
foldByKey
的泛化,后者又是
reduceByKey
的泛化

然而,我很难为这七种方法中的每一种找到简单的例子,而这七种方法只能用它们来表达,而不能用它们不那么一般的版本来表达。例如,我发现为
fold
提供了一个示例,但我也能够在相同的情况下使用
reduce

到目前为止,我发现:

  • 我读到更一般化的方法可能更有效,但这将是一个非功能性要求,我想得到一些无法用更具体的方法实现的示例
  • 我还读到,例如,传递给
    fold
    的函数必须是关联的,而
    reduce
    的函数必须是可交换的:(然而,我仍然不知道任何好的简单示例。)而在我读到的文章中,fold需要两个属性来保持
  • 我们可以将零值视为“添加所有元素并添加3”中的一个特性(例如,对于
    折叠
    超过
    减少
    ),并使用3作为零值,但这会产生误导,因为每个分区将添加3,而不是仅添加一次。此外,据我所知,这并不是折叠的目的——它不是作为一种功能,而是实现它的必要性,以便能够实现非交换功能
这七种方法的简单例子是什么

传递给fold的函数必须是关联的,而reduce的函数必须是可交换的

这是不对的fold
也要求函数是可交换的。这与
折叠
的操作不同,如中所述:

这与为非分布式系统实现的折叠操作有些不同 函数语言(如Scala)中的集合

折叠操作可应用于
单独分区,然后将这些结果折叠成最终结果,而不是
将
折叠
按定义的顺序依次应用于每个元素。功能 如果不是可交换的,则结果可能不同于应用于 非分布式收集

正如您所见,合并部分值的顺序不是契约的一部分,因此用于
折叠的函数必须是可交换的

我读到更普遍的方法可能更有效

从技术上讲,应该没有显著差异。对于
fold
vs
reduce

关于
*byKey
方法,所有方法都是使用相同的基本构造来实现的,即
将byKey与classtag组合起来,可以简化为三个简单的操作:

  • createCombiner
    -为给定分区创建“零”值
  • mergeValue
    -将值合并到累加器中
  • mergeCombiners
    -为每个分区创建的合并累加器

    • 让我们从逻辑上研究实际需要的东西

      首先,请注意,如果集合是无序的,则其上的任何(二进制)操作集都需要是可交换的和关联的,否则您将根据每次选择的(任意)顺序得到不同的答案。由于
      reduce
      fold
      aggregate
      都使用二进制操作,因此如果在无序(或被视为无序)的集合上使用这些操作,则所有内容都必须是可交换和关联的

      reduce
      是这样一种思想的实现,即如果可以将两个对象转换为一个对象,则可以将任意长的集合折叠为单个元素。关联性正是这样一种属性,即无论你如何配对,只要你最终将它们配对,并保持从左到右的顺序不变,那么这正是你所需要的

      a   b   c   d          a   b   c   d           a   b   c   d
      a # b   c   d          a # b   c   d           a   b # c   d
      (a#b)   c # d          (a#b) # c   d           a   (b#c)   d
      (a#b) # (c#d)          ((a#b)#c) # d           a # ((b#c)#d)
      
      只要操作(这里称为
      #
      )是关联的,上述所有操作都是相同的。没有理由交换哪些东西在左边,哪些东西在右边,因此操作不需要是可交换的(加法是:a+b==b+a;concat不是:ab!=ba)

      reduce
      在数学上很简单,只需要一个关联运算

      但是,Reduce是有限的,因为它不适用于空集合,并且不能更改类型。如果按顺序工作,可以创建一个函数,该函数接受新类型和旧类型,并生成具有新类型的内容。这是一个顺序折叠(如果新类型在左侧,则为左折叠,如果在右侧,则为右折叠)。这里没有关于操作顺序的选择,所以交换性、结合性和所有东西都是无关的。只有一种方法可以按顺序浏览列表。(如果希望左折叠和右折叠始终相同,则操作必须是关联的和可交换的,但由于左折叠和右折叠通常不会意外交换,因此确保这一点并不十分重要。)

      当你想并行工作时,问题就来了。你不能按顺序浏览你的收藏;从定义上讲,这不是平行的!因此,您必须在多个位置插入新类型!让我们调用我们的折叠操作
      @
      ,我们会说新类型在左边。此外,我们要说的是,我们总是从同一个元素开始,
      Z
      。现在我们可以做任何一件事了
        a     b     c     d        a     b     c     d         a     b     c     d
       Z@a    b     c     d       Z@a    b    Z@c    d        Z@a   Z@b   Z@c   Z@d
      (Z@a) @ b     c     d      (Z@a) @ b   (Z@c) @ d
      ((Z@a)@b)  @  c     d
      (((Z@a)@b)@c)    @  d
      
        a     b     c     d        a     b     c     d         a     b     c     d
       Z@a    b     c     d       Z@a    b    Z@c    d        Z@a   Z@b   Z@c   Z@d
      (Z@a) @ b     c     d      (Z@a) @ b   (Z@c) @ d        Z@a $ Z@b   Z@c $ Z@d
      ((Z@a)@b)  @  c     d      ((Z@a)@b) $ ((Z@c)@d)    ((Z@a)$(Z@b)) $ ((Z@c)$(Z@d))
      (((Z@a)@b)@c)    @  d
      
      (0.0+2) * (0.0+3) == 2.0 * 3.0 == 6.0
      ((0.0+2) + 3)     == 2.0 + 3   == 5.0
      
        a   b   c   d             () <- empty
       z#a z#b                    z
       z#a (z#b)#c
       z#a ((z#b)#c)#d
      (z#a)#((z#b)#c)#d