函数式编程-函数运算(2)
2021-07-28 本文已影响0人
Fantast_d2be
柯里化
- 使用柯里化解决上一个案例中硬编码的问题
function checkAge (age) { let min = 18
return age >= min
}
// 普通纯函数
function checkAge (min, age) {
return age >= min
}
checkAge(18, 24)
checkAge(18, 20)
checkAge(20, 30)
// 柯里化
function checkAge (min) {
return function (age) {
return age >= min
} }
// ES6 写法
let checkAge = min => (age => age >= min)
let checkAge18 = checkAge(18)
let checkAge20 = checkAge(20)
checkAge18(24)
checkAge18(20)
-
柯里化(Curring):
- 当一个函数有多个参数的时候先传递一部分参数调用它(这部分参数以后永远不变)
- 然后返回一个新的函数接收剩余的参数,返回结果
lodash中的柯里化函数
- _.curry(func)
- 功能:创建一个函数,该函数接收一个或多个func的参数,如果func所需要的参数都被提供则执行func并返回执行的结果。否则继续返回该函数并等待接收剩余的参数。
- 参数:需要柯里化的函数
- 返回值:柯里化后的函数
- 案例
const _ = require('lodash')
const match = _.curry(function(reg, str){
return str.match(reg)
})
const haveSpace = match(/\s+/g)
const haveNumber = match(/\d+/g)
console.log(haveSpace('hello world'))
console.log(haveNumber('25$'))
const filter = _.curry(function (func, array) {
return array.filter(func)
})
console.log(filter(haveSpace, ['John Connor', 'John_Donne']))
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)))
}
}
return func(...args)
}
}
总结
- 柯里化可以让我们给一个函数传递较少的参数得到一个已经记住了某些固定参数的新函数
- 这是一个对函数参数的'缓存'
- 让函数变的更灵活,让函数的粒度更小
- 可以把多元函数转换成一元函数,可以组合使用函数产生强大的功能
函数组合
- 纯函数和柯里化很容易写出洋葱代码h(g(f(x)))
// 获取数组的最后一个元素再转换成大写字母
_.toUpper(_.first(_.reverse(array)))
-
函数组合(compose):如果一个函数要经过多个函数处理才能得到最终值,这个时候可以把中间过程的函数合并成一个函数
- 函数就像是数据的管道,函数组合就是把这些管道连接起来,让数据穿过多个管道形成最终结股票
- 函数组合默认是从右到左执行
// 组合函数 function compose (f, g) { return function (x) { return f(g(x)) } } function first (arr) { return arr[0] } function reverse (arr) { return arr.reverse() } // 从右到左运行 let last = compose(first, reverse) console.log(last([1, 2, 3, 4]))
-
函数组合可以让我们把细粒度的函数重新组合生成一个新的函数
-
模拟实现组合函数方法
// 多函数组合
function compose(...fns) {
return function (value) {
return fns.reverse().reduce(function (acc, fn) {
return fn(acc)
},value)
}
}
-
函数的组合要满足结合律
- 我们既可以把g和h组合,还可以把f和g组合,结果都是一样的
// 结合律(associativity) let f = compose(f, g, h) let associative = compose(compose(f, g), h) == compose(f, compose(g, h)) // true
调试
const _ = require('lodash')
const trace = _.curry((tag, v) => { console.log(tag, v)
return v
})
const split = _.curry((sep, str) => _.split(str, sep))
const join = _.curry((sep, array) => _.join(array, sep))
const map = _.curry((fn, array) => _.map(array, fn))
const f = _.flowRight(join('-'), trace('map 之后'), map(_.toLower), trace('split 之后'), split(' '))
console.log(f('NEVER SAY DIE'))
Point Free
Point Free:我们可以把数据处理的过程定义成于数据无关的合成运算,不需要用到代表数据的那个参数,只要把简单的运算步骤合成到一起,在使用这种模式之前我们需要定义一些辅助的基本运算函数。
- 不需要指明处理的数据
- 只需要合成运算过程
- 需要定义一些辅助的基本运算函数
const f = fp.flowRight(fp.join('-'), fp.map(_.toLower), fp.split(' '))
- 案例演示
// 非 Point Free 模式
// Hello World => hello_world function f (word) {
return word.toLowerCase().replace(/\s+/g, '_'); }
// Point Free
const fp = require('lodash/fp')
const f = fp.flowRight(fp.replace(/\s+/g, '_'), fp.toLower)
console.log(f('Hello World'))
- 使用 Point Free 的模式,把单词中的首字母提取并转换成大写
const fp = require('lodash/fp')
const firstLetterToUpper = fp.flowRight(join('. '),
fp.map(fp.flowRight(fp.first, fp.toUpper)), split(' '))
console.log(firstLetterToUpper('world wild web'))