媒介
上一篇文章引见了javascript
中的compose
函数的完成,我是用了递归的头脑去让函数顺次实行,lodash
中是用了迭代的头脑顺次实行函数,但完成了今后我照样以为有些别扭,细致想一想,我们完成的是一个函数式编程用到的函数,然则完成的要领照样太敕令式了,函数照样敕令式的实行,浅显点说,照样太把函数当做函数了,在我的明白中,函数和一般变量没什么区分,只是实行的要领不一样,一旦给予了函数这个实行的属性,我们就能够完整将函数当做一般变量去看待。
函数和一般变量没什么区分,只是须要偶然实行一下
完成
1.函数天下的加号
举个例子
1 + 2 = 3
'a' + 'b' = 'ab'
func1 '+' func2 -> func3
前两个例子就是一般变量的操纵,末了一个例子是函数的操纵,本质上看来,没有任何区分,两个函数作用的结果就是天生一个函数,只不过在函数的天下里,这个加号的意义就是怎样变更天生一个新的函数,回到compose
来,在compose
中,加号的意义就是把一个函数的实行结果当做下一个函数的输入,末了在天生一个函数,就像下面如许
var fn = (func1, func2) => (...args) => func2.call(this, func1.apply(this, args))
在这个例子内里,func1
的实行结果就是func2
的参数,而且天生了一个新的函数fn
,我们给这个fn
通报参数,它就会作为func1
的参数来启动实行,末了获得了函数顺次实行的结果,这就是最简朴的compose
,这个函数就是ramda.js
完成compsoe
须要的第一个函数_pipe
var _pipe = (f, g) => (...args) => g.call(this, f.apply(this, args))
_pipe
就定义了compose
中所谓加号的意义了。
2.’不一样的’reduce
在这里提到了reduce
,是否是有一点觉得,reduce
的作用就是让一个数组不停的实行下去,所以肯定能和我们这个compose
有点联络,先举个reduce
最经常使用的例子,求数组的和
var a = [1,2,3,4,5]
a.reduce((x, y) => x + y, 0)
这个就是不停的将两个数乞降,天生一个新的数,再去和下一个数乞降,末了获得15
,下面想一下,如果把数字换成函数会怎样,两个函数连系天生一个新的函数,这个连系轨则就使用上面的_pipe,这个新的函数再去连系下一个函数,直到末了一个函数实行完,我们获得的照样函数,我们前面说了,函数学问偶然须要实行一下,这个函数的天生和实行历程是反向递归的历程。应用这个头脑,就能够寥寥几行(以至只须要一行)就写出来这个异常函数式的compose
了
var reverse = arr => arr.reverse()
var _pipe = (f, g) => (...args) => g.call(this, f.apply(this, args));
var compose = (...args) => reverse(args).reduce(_pipe, args.shift())
举个例子考证一下,我们把首个函数做多元处置惩罚,再upperCase
,再repeat
var classyGreeting = (firstName, lastName) => "The name's " + lastName + ", " + firstName + " " + lastName
var toUpper = str => str.toUpperCase()
var repeat = str => str.repeat(2)
var result = compose(repeat, toUpper, classyGreeting)('dong', 'zhe')
// THE NAME'S ZHE, DONG ZHETHE NAME'S ZHE, DONG ZHE
我在这里把函数天生历程剖析一下
起首我们用_pipe
组合classyGreeting
,toUpper
f1 = _pipe(classyGreeting, toUpper)
f1 = (...args) => toUpper.call(this, classyGreeting.apply(this, args))
_pipe
继承连系f1
, repeat
f2 = _pipe(f1, repeat)
f2 = (...args) => repeat.call(this, f1.apply(this, args))
函数的实行历程就会将参数层层通报到最内里的classyGreeting
最先实行,从而完成函数的顺次实行。ramda.js
本身完成了reduce
,不仅支撑数组的reduce
,还支撑多种数据结构的reduce
,(兼容性也更好?),下一步来剖析是怎样本身完成数组的reduce
的,可与看出,本身星散出来逻辑以后,函数的实行历程和组合的划定规矩部份将星散的更完全。
3.本身写一个reduce
reduce
吸收三个参数,实行函数,初始值,实行行列(能够不止为一个数组),返回一个针对这些参数的reduce
处置惩罚,这里只写数组部份(_arrayReduce
),源码中还包含了关于迭代器的_iterableReduce
等等,而且ramda.js
对实行函数也有一层对象封装,扩大了函数的功用
var reduce = (fn, acc, list) => (fn = _xwrap(fn), _arrayReduce(fn, acc, list))
在写_arrayReduce
之前,先来看一下函数的对象封装_xwrap
var _xwrap = (function(){
function XWrap(fn) {
this.f = fn;
}
XWrap.prototype['@@transducer/init'] = function() {
throw new Error('init not implemented on XWrap');
};
XWrap.prototype['@@transducer/result'] = function(acc) {
return acc;
};
XWrap.prototype['@@transducer/step'] = function(acc, x) {
return this.f(acc, x);
};
return function _xwrap(fn) { return new XWrap(fn); };
})()
实在就是对函数实行状况做了一个分类治理
@@transducer/step
这类状况认为是一种历程状况
@@transducer/result
这类状况被认为是一种结果状况
这类状况治理经由过程对象也是通情达理的
末了再来完成_arrayReduce
,就很简朴了,这个函数只是用心一件事变,就是写reduce
的历程划定规矩。
var _arrayReduce = (xf, acc, list) => {
var idx = 0
var len = list.length
while (idx < len) {
acc = xf['@@transducer/step'](acc, list[idx]);
idx += 1;
}
return xf['@@transducer/result'](acc);
}
至此,ramda.js
简化版的reduce
就完成了。
4.其他一些功用
tail
用来星散初始值和实行行列的,由于初始函数是多元的(吸收多个参数),实行行列都是一元(吸收一个参数)的,星散照样有必要的
var tail = arr => arr.slice(1)
reverse
转变实行递次
var reverse = arr => arr.reverse()
_arity
我把源代码贴出来,我也不知道为何如许做,多是明白指定参数吧,由于reduce
天生的函数是能够吸收多个参数的,_arity
就是处置惩罚这个函数的
var _arity = (n, fn) => {
switch (n) {
case 0: return function() { return fn.apply(this, arguments); };
case 1: return function(a0) { return fn.apply(this, arguments); };
case 2: return function(a0, a1) { return fn.apply(this, arguments); };
case 3: return function(a0, a1, a2) { return fn.apply(this, arguments); };
case 4: return function(a0, a1, a2, a3) { return fn.apply(this, arguments); };
case 5: return function(a0, a1, a2, a3, a4) { return fn.apply(this, arguments); };
case 6: return function(a0, a1, a2, a3, a4, a5) { return fn.apply(this, arguments); };
case 7: return function(a0, a1, a2, a3, a4, a5, a6) { return fn.apply(this, arguments); };
case 8: return function(a0, a1, a2, a3, a4, a5, a6, a7) { return fn.apply(this, arguments); };
case 9: return function(a0, a1, a2, a3, a4, a5, a6, a7, a8) { return fn.apply(this, arguments); };
case 10: return function(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) { return fn.apply(this, arguments); };
default: throw new Error('First argument to _arity must be a non-negative integer no greater than ten');
}
}
5.整合
末了整合出来两个终究的函数pipe
和compose
var pipe = (...args) => _arity(args[0].length, reduce(_pipe, args[0], tail(args)))
var remdaCompose = (...args) => pipe.apply(this, reverse(args))
再把上面的demo
试一下
console.log(remdaCompose(repeat, toUpper, classyGreeting)('dong', 'zhe'))
// THE NAME'S ZHE, DONG ZHETHE NAME'S ZHE, DONG ZHE
整合的完整版我放到了github里
总结
这篇文章重要剖析了ramda.js
完成compose
的历程,个中剖析了怎样把函数算作一等国民,怎样完成一个reduce
等等。能够看出,compose
的完成从头至尾都是函数式编程的头脑,下一篇文章盘算连系社区的一道问答题来引见一下怎样用函数式头脑来解决问题。我也是初学函数式,有什么说的不正确的处所愿望多多斧正。