Node-JS专精04柯里化

柯里化 Currying

让所有函数只接受一个参数

单一参数的意义?

基于单一参数,已经衍生出非常多的理论知识,如λ演算

那么如何支持两个参数?

用对象接受?

并不是,而是用闭包

还记得吗:对象是穷人的闭包

单参数函数接受两个参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 这是用对象形式
const add = ({a,b})=> a + b
add({a:1,b:2})

// add接受一个数字,返回一个函数
const add = a => b => a + b
add(1)(2)
/*
// 等价形式
const add = function(a){
return function(b){
return a + b;
}
}
*/

感觉有点不方便是不是

没错,如果你没接受函数式的一整套理论,你就没必要使用柯里化。

柯里化一个函数

把多个参数函数,变成单参数函数

面试题

如何把三参数函数 add(1,2,3) 变成 curriedAdd(1)(2)(3)形式?

1
2
3
4
5
6
const curriedArr =
a =>
b =>
c =>
add(a,b,c)
// 完毕

面试题升级

1
2
3
4
5
6
7
8
9
假设
addTwo 接受2个参数
addThree 接受3个参数
addFore 接受4个参数
请写出一个 currify函数,使他分别接受 2 3 4 次参数,比如
currify(addTwo)(1)(2) // 3
currify(addThree)(1)(2)(3) // 6
currify(addFore)(1)(2)(3)(4) // 10
也就是说,currify 能将任意接受固定个参数的函数,变成单一参数的函数

答案:

1
2
3
4
5
6
7
8
9
10
11
12
13
addTwo = (a,b)=>a+b
currify = (fn,params = [])=>{
return (arg)=>{
params.push(arg)
if(params.length === fn.length){
return fn(...params)
}else{
return currify(fn,params)
}
}
}
newAddTwo = currify(addTwo)
newAddTwo(1)(2) // 打印 3

代码是有问题的

1
2
3
4
5
// 如果这样就报错 
console.log(newAddTwo(1))
console.log(newAddTwo(1)(2))

原因是 params 是外面传递进来,会不会导致感染?

改进

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
addTwo = (a,b)=>a+b
currify = (fn,params = [])=>{
return (arg)=>{
const newParams = params.concat(arg)
if(newParams.length === fn.length){
return fn(...newParams)
}else{
return currify(fn,newParams)
}
}
}
newAddTwo = currify(addTwo)
newAddTwo(1)(2) // 打印 3
console.log(newAddTwo(1)) // 打印是一个函数
console.log(newAddTwo(1)(2)) // 打印3

再次优化

  • 它支持一次绑定两个参数
    • newAddThree(1)(2)(3) 可以这样 newAddThree(1)(2,3)
1
2
3
4
5
6
7
8
9
10
11
12
13
currify = (fn, params = [])=>
(...args) =>
params.length + args.length === fn.length
? fn(...params, ...args)
: currify(fn, [...params, ...args])

addTwo = (a, b)=>a + b
addThree = (a, b, c)=>a + b + c
newAddTwo = currify(addTwo)
newAddThree = currify(addThree)
newAddTwo(1)(2)
newAddThree(1)(2)(3)
newAddThree(1)(2,3)