reduce和fold区别

  • reduce
    • reduce源码
    • reduceLeft源码
      • 示例
    • reduceRight源码
    • reduceLeftOption源码
    • reduceRightOption源码
    • 总结
    • var acc: B = 0.asInstanceOf[B]
  • fold
    • fold源码
    • 解析
    • fold和foldLeft(存疑)
  • reduce和fold区别

reduce

reduce表示将列表,传入一个函数进行聚合运算。

reduce源码

  • [A1 >: A]:reduce的参数A1必须为调用reduce数据集元素类型的子集
  • reduceLeft(op):将匿名函数op传递给reduceLeft,底层调用reduceLeft实现
  • op: (A1, A1) => A1:第一个A1为当前聚合后的变量,第二个A1为当前要聚合的元素。最终返回A1类型的变量
  • reduce最终将原数据集聚合后生成一个元素
  def reduce[A1 >: A](op: (A1, A1) => A1): A1 = reduceLeft(op)

reduceLeft源码

  • 底层利用尾递归实现。调用reduce的数据集为空时抛出异常

实现过程

  1. var first = true:定义标记为first为true
  2. var acc: B = 0.asInstanceOf[B]:声明一个个泛型B类型相同的变量(这里用到了擦拭法,直接写0.asInstanceOf[String]会报错)
  3. 第一个元素时,将第一个元素的值赋给acc。同时将标记为置为false
  4. 从第二个元素开始,进行递归调用匿名函数op。直到遍历完整个源数据集
    • 将第一个元素和第二个元素进行操作,结果赋值给第一个元素,作为第一次聚合结果
    • 将第三个元素和第一次聚合结果进行操作,将结果赋给第一个元素,作为第二次聚合结果
    • 将第四个元素。。。。。
 /** Applies a binary operator to all elements of this $coll,*  going left to right.*  $willNotTerminateInf*  $orderDependentFold**  @param  op    the binary operator.*  @tparam  B    the result type of the binary operator.*  @return  the result of inserting `op` between consecutive elements of this $coll,*           going left to right:*           {{{*             op( op( ... op(x_1, x_2) ..., x_{n-1}), x_n)*           }}}*           where `x,,1,,, ..., x,,n,,` are the elements of this $coll.*  @throws UnsupportedOperationException if this $coll is empty.   */def reduceLeft[B >: A](op: (B, A) => B): B = {if (isEmpty)throw new UnsupportedOperationException("empty.reduceLeft")var first = truevar acc: B = 0.asInstanceOf[B]for (x <- self) {if (first) {acc = xfirst = false}else acc = op(acc, x)}acc}

示例

scala> val a = List(1,2,3,4,5,6,7,8,9,10)
a: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)//集合中第一个元素赋给x,第二个赋给y
//相加后的元素赋给x,第三个元素赋给y
//以此类推
scala> a.reduce((x,y) => x + y)
res5: Int = 55// 第一个下划线表示第一个参数,就是历史的聚合数据结果
// 第二个下划线表示第二个参数,就是当前要聚合的数据元素
scala> a.reduce(_ + _)
res53: Int = 55

reduceRight源码

  • 源数据集为空,则抛出异常
  • 源数据集不为空,从右侧开始递归
  def reduceRight[B >: A](op: (A, B) => B): B = {if (isEmpty)throw new UnsupportedOperationException("empty.reduceRight")reversed.reduceLeft[B]((x, y) => op(y, x))}```vbnet(x, y) => op(y, x) x,y位置互换
例:list.reduceRight(_-_) =》过程:
List(1,4,3,5,3) = 》List(3,5,3,4,1)=》5-3=》3-(5-3)=》4-(3-(5-3))=》1-(4-(3-(5-3))) 小括号内的结果,每次都放在后面一个位置

reduceLeftOption源码

  • 源数据将为空则返回None
  • 源数据集不为空则调用reduceLeft,将操作Op传递给reduceLeft
  def reduceLeftOption[B >: A](op: (B, A) => B): Option[B] =if (isEmpty) None else Some(reduceLeft(op))

reduceRightOption源码

  • 源数据将为空则返回None
  • 源数据集不为空则调用reduceLeft,将匿名函数Op传递给reduceRight
  def reduceRightOption[B >: A](op: (A, B) => B): Option[B] =if (isEmpty) None else Some(reduceRight(op))

总结

  • 从源码上看:干活的函数是reduceLeft。reduceLeft底层是利用尾递归实现功能
  • reduce的初始值为源数据第一个或者最后一个元素
  • 调用reduce的数据集为空时,会抛出异常:throw new UnsupportedOperationException("empty.reduceLeft")
  • reduce相关函数最后返回或者聚合生成一个元素(源数据集还在)
  • reduce最后聚合生成的元素类型与源数据类型一致

var acc: B = 0.asInstanceOf[B]

将foldLeft放入源文件并进行编译,以打印出中间代码阶段:

./scalac -Xprint:icode X.scala

然后你得到…

def reduceLeft($this: X, op$1: Function2): java.lang.Object = {if ($this.isEmpty())scala.sys.`package`.error("Bad")else();var first$1: scala.runtime.BooleanRef = new scala.runtime.BooleanRef(true);var acc$1: scala.runtime.ObjectRef = new scala.runtime.ObjectRef(scala.Int.box(0));$this.foreach({(new anonymous class X$$anonfun$reduceLeft$1($this, op$1, first$1, acc$1): Function1)});acc.elem
};

原文链接

fold

给定初始值,将源数据集和初始值一起进行折叠

fold源码

  • fold底层调用foldLeft(),foldLeft底层利用foreach实现。但是fold无序,为什么无序目前我还没搞清楚
  • fold相关函数都是柯里化函数
  • /: ( )和:()是foldLeft和foldRight的快速写法,斜杠前写初始值
  • 每次处理完的数据都赋值给集合外的元素,然后继续和后面的元素一起被操作

示例

scala> (0 /: List("1", "2", "3")) {(sum, elem) => sum + elem.toInt}
res2: Int = 6
scala> (0 /: List("1", "2", "3")) {_ + _.toInt}
res3: Int = 6
scala> (List("1", "2", "3") :\ 0) {(elem, sum) => elem.toInt + sum}
res13: Int = 6
scala> (List("1", "2", "3") :\ 0) {_.toInt + _}
res14: Int = 6

源码

 def fold[A1 >: A](z: A1)(op: (A1, A1) => A1): A1 = foldLeft(z)(op)def /:[B](z: B)(op: (B, A) => B): B = foldLeft(z)(op)def :\[B](z: B)(op: (A, B) => B): B = foldRight(z)(op)def foldLeft[B](z: B)(op: (B, A) => B): B = {var result = zthis foreach (x => result = op(result, x))result
}def foldRight[B](z: B)(op: (A, B) => B): B =reversed.foldLeft(z)((x, y) => op(y, x))

解析

  • 从源码上看,fold底层实现是利用foreach
  • 首先将第一个B类型的参数z赋值给变量result
  • 然后源数据集调用foreach(如List.foreach(),这里的this foreach 和this.foreach含义相同)
    • 在第一次调用foreach中,调用匿名函数op处理result和源数据集的某个元素。
    • 将Op的处理结果作为返回值赋值给result
  • 利用foreach处理源数据集的每一个元素,重复上面第一次调用的过程
  • 最后将foreach处理的结果,作为fold的结果

fold和foldLeft(存疑)

理论上fold和foldLeft一模一样,实际上fold可能是乱序的,foldLeft是从左到右执行。
foldLeft可以返回与源数据集不同的类型,而fold则可能报错。

scala> val ls = List("1","2","3")
ls: List[String] = List(1, 2, 3)scala> ls.foldLeft(0)(_+_.toInt)
res13: Int = 6scala> ls.fold(0)(_+_.toInt)
<console>:13: error: value toInt is not a member of Anyls.fold(0)(_+_.toInt)^
scala> ls.foldRight(0)(_+_.toInt)
<console>:13: error: type mismatch;found   : Stringrequired: Intls.foldRight(0)(_+_.toInt)
  • 从上面结果可以看到,当给fold的初始值可源数据类型不一致时,会出问题

网上的解释大多是:fold内部乱序。从源码上此处我没有搞明白。

下面是网上的解释
这是因为fold函数不讲究折叠顺序,foldLeft是这样结合的:

((0 + "1".toInt) + "2".toInt) + "3".toInt

而fold函数是乱序,有可能像上面这样,也有可能是下面这样或其他不可预测的顺序:

(0 + "2".toInt) + ("1" + "3".toInt).toInt

reduce和fold区别

  • 初始值

    • Reduce初始值是集合元素的头或尾(left或者right)
    • Fold必须手动指定初始值
  • 空集合操作
    • Reduce进行空集合操作会抛异常
    • Fold进行空集合操作结果为初始值
  • 返回值
    • Reduce最终聚合的元素必须和调用Reduce方法的集合中元素一致
    • Fold最终折叠得到的元素类型可以和调用Fold中的元素不一致(使用FoldLeft)
  • 底层实现
    • Reduce底层采用尾递归实现
    • Fold底层采用foreach实现

scala reduce和Fold相关推荐

  1. scala使用reduce和fold方法遍历集合的所有元素

    Problem 你想要遍历有序集合的所有元素,并且随着你对集合元素的遍历,对比两个相邻的元素 Solution 使用reduceLeft, foldLeft, reduceRight, foldRig ...

  2. Kotlin reduce、fold

    转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/121974841 本文出自[赵彦军的博客] reduce 从集合左到右,依次计算每个 ...

  3. kotlin之plus、copyOf、reverse、forEach、filter、map、reduce、fold等函数解释和使用

    kotlin之::函数调用.plus(增加元素).copyOf(复制数组).reverse(翻转数组).forEach(遍历数组).filter(过滤数组).map函数操作及扩展.reduce函数.f ...

  4. spark常用RDD算子 汇总(java和scala版本)

    github: https://github.com/zhaikaishun/spark_tutorial  spark RDD的算子挺多,有时候如何灵活的使用,该如何用一下子想不起来,这一段时间将s ...

  5. scala集合算子大全及分类汇总——倾心整理

    ++ 合并集合,返回新的数组 val arr1=Array(1,2,3) val arr2=Array(4,5,6) val arr3=arr1++arr2 arr3.foreach(println) ...

  6. Scala详细文本教学04

    前言 你们好我是啊晨,前些天偷懒了下 首先呢还是那句话,内容很多,分为几篇这是最后篇,选择阅读就好,很详细. 下面请: 一.Scala 方法 Scala 有方法与函数,二者在语义上的区别很小.Scal ...

  7. 018 The Scala Programming Language

    1.Scala介绍 Scala(百度百科) 为什么要学习Scala 为什么要使用 Scala 语言?Scala 语言的优势在哪里? 为什么选择Scala,它在大数据处理方面有何优势? 博主学习Scal ...

  8. 学习Scala — 8个不做和7个不做

    Today I would like to share with you my view on learning Scala, what approach to take, what to do an ...

  9. Scala学习笔记-详细记录学习中遇到的知识点

    目录 输入 print输出 变量 数据类型 标识符与关键字 运算符优先级 if else for循环 yield生成器 break与continue 函数 局部函数 惰性lazy 异常 类与对象 构造 ...

  10. 用scala写wordcount

    笨人勤总结,忘了再捡起来. 1.scala一些预热操作 1.1 to 是一个方法,()可以进行 参数传递,map()把每一个元素取出来进行相应的操作, print(1.to(10).map(_*10) ...

最新文章

  1. 文曲星猜数游戏,无测试代码
  2. mac系统更新后code .命令打不开vs code
  3. vaex 处理海量数据_核心业务“瘦身”进行时!手把手带你搭建海量数据实时处理架构...
  4. Spring+Dubbo集成Redis的两种解决方案
  5. 抓取沪A股票资金流向数据
  6. PLS-00103: Encountered the symbol “DECLARE“
  7. python绘制图像并渲染_用Python的Matplotlib模块绘制3D图像
  8. 脉冲经过高通和低通滤波器后的波形
  9. Lenovo UEFI引导U盘 System x Install Windows Server 2016 R2
  10. 黑马程序员武汉2019新版前端与移动开发学习路线图(视频+工具+书籍+资源)
  11. 液晶OLED接口MIPI之DSI协议学习
  12. Grafana更改主题背景
  13. 5G学习(四):PRACH专题
  14. PHP+MySQL实现博客管理系统
  15. LDAP 和 LDAP3 的对比、接口调用(1)
  16. C++ Primer 第六章—— 函数 思维导图
  17. 【2018】输出奇偶数之和
  18. vue中使用AES加密与AES解密
  19. android 3d魔方 代码,3D--魔方(示例代码)
  20. Vbox 未指定XXX网络名称 找不到网卡有关问题

热门文章

  1. ubuntu18 安装caj阅读器 wine
  2. CAN、485隔离电路分享
  3. 悟彻菩提真妙理 断魔归本合元神
  4. 今日芯声 | 从“罪犯”到“英镑人物”,图灵比肩英国女王
  5. Teams 的 Meeting App
  6. RHCE——控制服务和守护进程
  7. E - Skyscrapers (hard version)
  8. 【HBZ】生产环境下如何解决CPU飙高 与排查CPU飙高问题 与如何解决内存泄漏
  9. Linux系统编程 40 -open函数
  10. python关闭指定浏览器页面_Python自动关闭浏览器关闭网页的方法