reduce表示将列表,传入一个函数进行聚合运算。
def reduce[A1 >: A](op: (A1, A1) => A1): A1 = reduceLeft(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 = true
var acc: B = 0.asInstanceOf[B]
for (x <- self) {
if (first) {
acc = x
first = 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
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))) 小括号内的结果,每次都放在后面一个位置
def reduceLeftOption[B >: A](op: (B, A) => B): Option[B] =
if (isEmpty) None else Some(reduceLeft(op))
def reduceRightOption[B >: A](op: (A, B) => B): Option[B] =
if (isEmpty) None else Some(reduceRight(op))
throw new UnsupportedOperationException("empty.reduceLeft")
将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
};
给定初始值,将源数据集和初始值一起进行折叠
示例
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 = z
this 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和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 = 6
scala> ls.fold(0)(_+_.toInt)
<console>:13: error: value toInt is not a member of Any
ls.fold(0)(_+_.toInt)
^
scala> ls.foldRight(0)(_+_.toInt)
<console>:13: error: type mismatch;
found : String
required: Int
ls.foldRight(0)(_+_.toInt)
网上的解释大多是:fold内部乱序。从源码上此处我没有搞明白。
下面是网上的解释
这是因为fold函数不讲究折叠顺序,foldLeft是这样结合的:
((0 + "1".toInt) + "2".toInt) + "3".toInt
而fold函数是乱序,有可能像上面这样,也有可能是下面这样或其他不可预测的顺序:
(0 + "2".toInt) + ("1" + "3".toInt).toInt