scala reduce和Fold
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的数据集为空时抛出异常
实现过程:
- var first = true:定义标记为first为true
- var acc: B = 0.asInstanceOf[B]:声明一个个泛型B类型相同的变量(这里用到了擦拭法,直接写0.asInstanceOf[String]会报错)
- 第一个元素时,将第一个元素的值赋给acc。同时将标记为置为false
- 从第二个元素开始,进行递归调用匿名函数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相关推荐
- scala使用reduce和fold方法遍历集合的所有元素
Problem 你想要遍历有序集合的所有元素,并且随着你对集合元素的遍历,对比两个相邻的元素 Solution 使用reduceLeft, foldLeft, reduceRight, foldRig ...
- Kotlin reduce、fold
转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/121974841 本文出自[赵彦军的博客] reduce 从集合左到右,依次计算每个 ...
- kotlin之plus、copyOf、reverse、forEach、filter、map、reduce、fold等函数解释和使用
kotlin之::函数调用.plus(增加元素).copyOf(复制数组).reverse(翻转数组).forEach(遍历数组).filter(过滤数组).map函数操作及扩展.reduce函数.f ...
- spark常用RDD算子 汇总(java和scala版本)
github: https://github.com/zhaikaishun/spark_tutorial spark RDD的算子挺多,有时候如何灵活的使用,该如何用一下子想不起来,这一段时间将s ...
- scala集合算子大全及分类汇总——倾心整理
++ 合并集合,返回新的数组 val arr1=Array(1,2,3) val arr2=Array(4,5,6) val arr3=arr1++arr2 arr3.foreach(println) ...
- Scala详细文本教学04
前言 你们好我是啊晨,前些天偷懒了下 首先呢还是那句话,内容很多,分为几篇这是最后篇,选择阅读就好,很详细. 下面请: 一.Scala 方法 Scala 有方法与函数,二者在语义上的区别很小.Scal ...
- 018 The Scala Programming Language
1.Scala介绍 Scala(百度百科) 为什么要学习Scala 为什么要使用 Scala 语言?Scala 语言的优势在哪里? 为什么选择Scala,它在大数据处理方面有何优势? 博主学习Scal ...
- 学习Scala — 8个不做和7个不做
Today I would like to share with you my view on learning Scala, what approach to take, what to do an ...
- Scala学习笔记-详细记录学习中遇到的知识点
目录 输入 print输出 变量 数据类型 标识符与关键字 运算符优先级 if else for循环 yield生成器 break与continue 函数 局部函数 惰性lazy 异常 类与对象 构造 ...
- 用scala写wordcount
笨人勤总结,忘了再捡起来. 1.scala一些预热操作 1.1 to 是一个方法,()可以进行 参数传递,map()把每一个元素取出来进行相应的操作, print(1.to(10).map(_*10) ...
最新文章
- 文曲星猜数游戏,无测试代码
- mac系统更新后code .命令打不开vs code
- vaex 处理海量数据_核心业务“瘦身”进行时!手把手带你搭建海量数据实时处理架构...
- Spring+Dubbo集成Redis的两种解决方案
- 抓取沪A股票资金流向数据
- PLS-00103: Encountered the symbol “DECLARE“
- python绘制图像并渲染_用Python的Matplotlib模块绘制3D图像
- 脉冲经过高通和低通滤波器后的波形
- Lenovo UEFI引导U盘 System x Install Windows Server 2016 R2
- 黑马程序员武汉2019新版前端与移动开发学习路线图(视频+工具+书籍+资源)
- 液晶OLED接口MIPI之DSI协议学习
- Grafana更改主题背景
- 5G学习(四):PRACH专题
- PHP+MySQL实现博客管理系统
- LDAP 和 LDAP3 的对比、接口调用(1)
- C++ Primer 第六章—— 函数 思维导图
- 【2018】输出奇偶数之和
- vue中使用AES加密与AES解密
- android 3d魔方 代码,3D--魔方(示例代码)
- Vbox 未指定XXX网络名称 找不到网卡有关问题
热门文章
- ubuntu18 安装caj阅读器 wine
- CAN、485隔离电路分享
- 悟彻菩提真妙理 断魔归本合元神
- 今日芯声 | 从“罪犯”到“英镑人物”,图灵比肩英国女王
- Teams 的 Meeting App
- RHCE——控制服务和守护进程
- E - Skyscrapers (hard version)
- 【HBZ】生产环境下如何解决CPU飙高 与排查CPU飙高问题 与如何解决内存泄漏
- Linux系统编程 40 -open函数
- python关闭指定浏览器页面_Python自动关闭浏览器关闭网页的方法