说道reduce这个词, 想必JS Developer大多会用过underscore 2 (或类似)的reduce方法, 大概形式是这样
_.reduce(fn, 0, [1,2,3])
大概意思是初始为0, 应用fn到每一个collection(检测coll)元素上,得到一个新的值.
如果加上map, 比如(我要开始用 mori 3 了)
reduce(sum, 0, map(inc [1,2,3]))
Terminology:
让我们一步一步分析一下这次reduce到底干了什么
[2,3,4]
好吧这就是reduce了, 用一个reducing函数sum去计算coll得出一个新的值.
来看看更好的解法
reduce函数需要等待map返回新的coll后才能reduce, 那么可不可以一步直接算出来呢?
假如我们有一个函数xf可以变换reducing函数(上例的sum是reducing函数)的形式, 比如
xf(reduceFn) -> anotherReduceFn
再假如我们的新map函数可以做这种转换
map(inc)(sum) -> aShinyNewReduceFn
map 函数的简单transform实现可以这样实现,如果你感兴趣的话
function map(fn){ return function(reduceFn){ return function(result, input){ reduceFn(result, fn(input)) } } }
那么我们之前的reduce就可以写成
reduce(map(inc)(sum),0,[1,2,3])
yeah, 现在只需要一步就reduce出来结果了, reduce应用 map(inc)(sum)
来计算值, 只需要遍历一遍coll
但是如果我们不想改变map函数的接口, 原始形式的接口还是比较好写好读的
reduce(sum, 0, map(inc [1,2,3]))
那么需要进一步的抽象, 我把新的map函数叫做rmap好了
function rmap(fn, coll){ reducer(coll, map(fn)) }
跟以前接口一样,接收函数和coll,但是返回一个由reducer生成的reducible, 所以就变成了
reduce(sum, 0, reducer([1,2,3], map(inc)))
等等,怎么做到的…你已经消费了coll了, 那reducing函数怎么进来的, reducer怎么知道用sum去reduce呢.
答案是, 反转reduce的关系, 原来reduce用sum去计算结果, 现在,我们调用reducible的reduce方法来计算结果
如果你还没有被我弄晕的话, 准备好, 又来一个新单词 reducible . 也就是可以被reduce的东西.
于是我们需要coll实现reduce方法,这样就成为reducible了.
也就是reduce函数现在应该长这样, 我们暂且叫它 rreduce
function rreduce(reduceFn, init, reducible){ reducible(reduceFn, init) }
那么我们的例子就变成了这样
reducer([1,2,3], map(inc))(sum, 0)
reducer接收coll和xf, 返回reducible函数. 这一切都是lazy的, 直到rreduce调用第coll行才执行.
function reducer(coll, xf){ return function(reduceFn, init){ return coll.reduce(xf(reduceFn), init) (coll) } }