函数式编程之柯里化
Currying
接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术
固定一个可以预期的参数,并返回一个特定的函数,处理批特定的需求
关键词:参数复用
返回一个特定函数
返回的函数接收余下的参数
批处理
延迟运行
预习
call 、 apply区别
通用实现方法
const currying = (fn, ...args) => (...innerArgs) => fn.apply(null, args.concat(innerArgs))
特点
降低适用范围、提高适用性
例子: 重复的 处理一组数据中的每个元素
const arrA = [1, 2, 3]
const arrB = [4, 5]
const arrC = [6, 7, 8]
// 处理成 自乘,加倍
解决方案:
- 通用函数
// 自乘
const square = i => i * i
// 加倍
const double = i => i * 2
// 通用函数
const map = (handler, list) => list.map(handler)
map(square, arrA)
map(square, arrB)
map(square, arrC)
map(double, arrA)
map(double, arrB)
map(double, arrC)
通用函数,对通用性增强,带来了对适用性的减弱
- 柯里化
const squareCurry = currying(map, square)
squareCurry(arrA)
squareCurry(arrB)
squareCurry(arrC)
const doubleCurry = currying(map, double)
doubleCurry(arrA)
doubleCurry(arrB)
doubleCurry(arrC)
延迟执行
不断的柯里化,累积传入的参数,最后才输出
例子:
add(1)(2) // => 3
const add = (pre) => (next) => pre + next
add(1)(5)(8)(6)(3)(3)(7)()
function add (num) {
let sum = num
return function callback (innerNum) {
if (!arguments.length) {
return sum
}
sum = sum += innerNum
return callback
}
}
console.log(add(1)(2)(3)())
使用柯里化,通用写法
const currying = (fn) => {
const args = []
return function callBack() {
if (arguments.length == 0) {
// 需要在这里考虑执行环境
return fn.apply(this, args)
}
Array.prototype.push.apply(args, arguments);
return callBack;
}
}
const sum = (...nums) => {
let result = 0
nums.forEach(num => result += num)
return result
}
const sumCurrying = currying(sum)(1)(2)(3)
console.log(sumCurrying())
拓展之反柯里化
非我之物,为我所用
创建一个更普适性的函数,可以被不同的对象使用
提高适用范围,降低适用性
与我无关的展示方法,使用它去展示我
function Toast (option) {
this.prompt = ''
}
Toast.prototype = {
constructor: Toast,
// 输出提示
show: function () {
console.log(this.prompt)
}
}
// 新对象
var obj = {
prompt: '新对象'
};
function unCurrying(fn) {
return function (arg, ...other) {
return fn.apply(arg, other)
}
}
var objShow = unCurrying(Toast.prototype.show)
objShow(obj)
通用unCurrying
Function.prototype.unCurrying = function () {
const self = this
return function () {
return Function.prototype.call.apply(self, arguments)
// Function.prototype.call.apply
// 可以理解为 --> callFunction.apply()
// 将callFunction的this指针,通过apply指向self,并传入参数arguments
// callFunction(arguments)
// --> callFunction(arg[0], arg[1-n])
// Function.prototype.call.apply(Toast.prototype.show, [obj])
// -->
// Toast.prototype.show.call(obj)
// -->
// obj.show()
}
}
const objShow = Toast.prototype.show.unCurrying()
objShow(obj)
const slice = Array.prototype.slice.unCurring()
slice(arguments)
函数式编程高阶应用
- 范畴论
函数式编程的起源,是一门范畴论的数学分支
"范畴就是使用箭头连接的物体。"
(In mathematics, a category is an algebraic structure that comprises "objects" that are linked by "arrows". )
Effect
函子of
方法Maybe
函子Either
函子ap
函子Monad
函子- 将不纯的
I/O
(输入/输出)操作写成Monad
函子来完成