reduce最普通方法
其中fold
“写一个函数 fold。它提取出 sum 和 prod 的共同点,把不同点作为参数传进去,这样 fold 可以表示 sum 和 prod 的语义。它的工作原理类似第二课的 accum,只不过它的输入是一个链表的数字。”
function fold(ls, unit, combine)
{
// TODO
}
show(fold(list1, 0, (x, y) => x + y)); // 21, 相当于 sum(list1)
show(fold(list1, 1, (x, y) => x * y)); // 378,相当于 prod(list1)
Here, the identity element is both an initial seed value for the reduction and a default result if there are no input elements. The accumulator function takes a partial result and the next element, and produces a new partial result. The combiner function combines two partial results to produce a new partial result. (The combiner is necessary in parallel reductions, where the input is partitioned, a partial accumulation computed for each partition, and then the partial results are combined to produce a final result.)
More formally, the identity value must be an identity for the combiner function. This means that for all u, combiner.apply(identity, u) is equal to u. Additionally, the combiner function must be associative and must be compatible with the accumulator function: for all u and t, combiner.apply(u, accumulator.apply(identity, t)) must be equals() to accumulator.apply(u, t).
The three-argument form is a generalization of the two-argument form, incorporating a mapping step into the accumulation step. We could re-cast the simple sum-of-weights example using the more general form as follows:
int sumOfWeights = widgets.stream()
.reduce(0,
(sum, b) -> sum + b.getWeight())
Integer::sum);
其中的Integer::sum相当于:
如果 f 是一个单参数的函数,那么 x => f(x) 等价于 f。先想一下这是为什么?
如果 f 是多参数的函数,也有类似的等价关系,比如 (x, y) => f(x, y) 等价于 f。
为什么accumulator中第一个元素是sum,为什么不是第二个元素是sum,也就是sum和b互换位置为什么不可以?
因为:注释中有解释
The accumulator function takes a partial result and the next element, and produces a new partial result.
accumulator携带一个部分结果和下一个元素,并且生产一个新的部分结果
同样combiner也是将两个部分结果合并成一个新的部分结果
The combiner function combines two partial results to produce a new partial result.
该方法可以使用map和reduce的组合来进行代替
Many reductions using this form can be represented more simply by an explicit combination of {@code map} and {@code reduce} operations.
/**
* Performs a reduction on the
* elements of this stream, using the provided identity, accumulation and
* combining functions. This is equivalent to:
* {@code
* U result = identity;
* for (T element : this stream)
* result = accumulator.apply(result, element)
* return result;
* }
*
* but is not constrained to execute sequentially.
*
* The {@code identity} value must be an identity for the combiner
* function. This means that for all {@code u}, {@code combiner(identity, u)}
* is equal to {@code u}. Additionally, the {@code combiner} function
* must be compatible with the {@code accumulator} function; for all
* {@code u} and {@code t}, the following must hold:
*
{@code
* combiner.apply(u, accumulator.apply(identity, t)) == accumulator.apply(u, t)
* }
*
* This is a terminal
* operation.
*
* @apiNote Many reductions using this form can be represented more simply
* by an explicit combination of {@code map} and {@code reduce} operations.
* The {@code accumulator} function acts as a fused mapper and accumulator,
* which can sometimes be more efficient than separate mapping and reduction,
* such as when knowing the previously reduced value allows you to avoid
* some computation.
*
* @param The type of the result
* @param identity the identity value for the combiner function
* @param accumulator an associative,
* non-interfering,
* stateless
* function for incorporating an additional element into a result
* @param combiner an associative,
* non-interfering,
* stateless
* function for combining two values, which must be
* compatible with the accumulator function
* @return the result of the reduction
* @see #reduce(BinaryOperator)
* @see #reduce(Object, BinaryOperator)
*/
U reduce(U identity,
BiFunction accumulator,
BinaryOperator combiner);