为什么要使用柯里化函数
上一篇文章提到函数式编程很容易写出洋葱代码,不好维护,柯里化函数能解决这个问题
什么是柯里化函数
柯里化函数是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。
- 当一个函数有多个参数的时候先传递一部分参数调用它(这部分参数以后永远不变)
- 然后返回一个新的函数接收剩余的参数,返回结果
lodash中的柯里化函数
- _.curry(func)
功能:创建一个函数,该函数接收一个或多个 func 的参数,如果 func 所需要的参数都被提供则执行 func 并返回执行的结果。否则继续返回该函数并等待接收剩余的参数。
参数:需要柯里化的函数
返回值:柯里化后的函数
基础用法:
const _ = require('lodash')
// 要柯里化的函数
function getSum (a, b, c) { return a + b + c }
// 柯里化后的函数
let curried = _.curry(getSum)
curried(1, 2, 3)
curried(1)(2)(3)
curried(1, 2)(3)
案例:
const _ = require('lodash')
const match = _.curry(function (reg, str) {
return str.match(reg)
})
const haveSpace = match(/\s+/g)
const filter = _.curry(function (func, array) {
return array.filter(func)
})
const findSpace = filter(haveSpace)
console.log(findSpace(['John Connor', 'John_Donne']))
模拟_.curry()的实现
function curry(func) {
return function curriedFn(...args) {
// 判断实参和形参的个数
if (args.length < func.length) {
return function () {
return curriedFn(...args.concat(Array.from(arguments)))
}
}
// 实参和形参个数相同,调用 func,返回结果
return func(...args)
}
}
函数组合
- 纯函数和柯里化很容易写出洋葱代码 h(g(f(x)))
- 函数组合可以让我们把细粒度的函数重新组合生成一个新的函数
管道:补充知识
下图数据a通过管道得到数据b
graph LR
a --> fn --> b
下面这张图中可以想象成把 fn 这个管道拆分成了3个管道 f1, f2, f3,数据 a 通过管道 f1 得到结果 m,m 再通过管道 f2 得到结果 n,n 通过管道 f3 得到最终结果b
graph LR
a --> f1 --> f2 --> f3 --> b
fn = compose(f1, f2, f3)
b = fn(a)
函数组合
// 组合函数初级版
const compose = (f, g) => x => f(g(x))
// 多函数组合 默认是从右到左执行
// acc:上一次调用返回值, fn:fns数组中当前被处理的元素
const compose = (...fns) =>
value => fns.reduceRight((acc, fn) => fn(acc), value)
// 案例:输出数组最后一项的大写
const first = arr => arr[0]
const reverse = arr => arr.reverse()
const toUpper = s => s.toUpperCase()
const lastItemToUpper = compose(toUpper, first, reverse)
console.log(lastItemToUpper(['apple', 'banana', 'grape']))
函数的组合要满足结合律
const lastItemToUpper = compose(compose(toUpper, first), reverse)
const lastItemToUpper = compose(toUpper, compose(first, reverse))
调试
const trace = curry((tag, v) => {
console.log(tag, v)
return v
})
const lastItemToUpper = compose(
toUpper,
trace('first之后'),
first,
trace('reverse之后'),
reverse
)
实际应用
lodash 的 fp 模块提供了实用的对函数式编程友好的方法
const fp = require('lodash/fp')
const f = fp.flowRight(fp.join('-'), fp.map(_.toLower), fp.split(' '))
console.log(f('NEVER SAY DIE'))
在实际开发过程中可以采用小步快跑的形式,将函数式编程,柯里化函数逐渐应用到实际开发中。