在学习完纯函数之后,函数式编程就已经开始慢慢成型了,因为函数式编程本质上就是将各种纯函数组合在一起来实现功能,而本篇则是对纯函数的一层封装,以一种特性的组合方式将所有传入的纯函数列表进行组合调用,从而实现对函数式编程的另一种理解。
核心概念
函数组合,也叫饲养函数(compose)。为什么会叫饲养函数?可以理解为将其他的小的纯函数、偏函数、柯里化函数喂养给最后的一个大的功能函数,让这个功能变膘变壮,以完成最后的功能。举个例子:
// 传统方式
function toUpperCase(str) {
return str.toUpperCase()
}
function exclaim(str) {
return str + '!'
}
// 传统调用方式
console.log(exclaim(toUpperCase('hello'))) // HELLO!
// 使用 compose 的组合方式:
const composing = compose(exclaim, toUpperCasem) // 返回一个新的函数
composing('hello') // 调用组合好的函数集合
对比直接调用的方法,函数组合的好处是跟容易进行函数复用,而 compose
函数的定义如下:
function compose(f, g) {
return function(x) {
return f(g(x))
}
}
很简单的一个函数对不对,实际上核心的代码是第三行的 f(g(x))
,它通过自右向左组合函数并调用最后传入参数执行,得到最终的效果,这种组织代码的方式,被叫做 左倾
。简单来讲,左倾
就是将一个函数的执行放在另一个函数调用的 ()
中,将执行的函数返回值作为下一个执行函数的参数。以上 compose
函数虽然能满足需求,但是如果想要组合的函数再多一个的话,它就不得不进行修改了,所以这肯定不是最终方案。
改造函数
function compose(...fns) {
// 返回一个函数,并将接收到的函数传给最初传入的函数列表,函数列表会从右向左执行函数,最终返回最后一个函数的返回值
return (value) => fns.reduceRight((params, fn) => fn(params), value)
}
我们再对这个函数传入更多函数进行测试:
function toUpperCase(str) {
return str.toUpperCase()
}
function exclaim(str) {
return str + '!'
}
function split(str) {
return str.split('')
}
function reverse(arr) {
return arr.reverse()
}
function join(arr) {
return arr.join('')
}
const composing = compose(exclaim, toUpperCase, join, reverse, split)
console.log(composing('hello')) // OLLEH!
结合律(Associativity)
结合律在 JS 编程中同样是一个比较少见的概念,它与数学中的加法结合律概念一致
加法结合律:三个数相加,先把前面两个数相加,再加第三个数,或者先把后面两个数相加,再和第一个数相加,它们的和不变。
const c1 = compose(join, reverse, split)('hello') // 'olleh'
const c2 = compose(compose(join, reverse), split)('hello') // 'olleh'
const c3 = compose(join, compose(reverse, split))('hello') // 'olleh'
结果如上,不管将他们的值怎么组合,只要函数传入的顺序不变,那么最终的结果也是不会变的。
Pointfree
Love means never having to say you’re sorry.
爱意味着永远不用说抱歉。
Pointfree style means never having to say your data.
而pointfree
意味着你永远不用主动表明你的数据。