柯里化函数
大约 2 分钟
概述
柯里化函数能将一个接受多个参数的函数,转换为一个接受单一参数的函数,其会返回另一个函数,并继续接受剩余的参数
如下所示,foo
和 bar
函数的返回结果均为 a + b + c = 10,调用的方式却不相同。foo
函数一次性将所有参数传入,而 bar
函数则是逐个参数传入。foo
函数转换为 bar
函数的过程,即 currying 柯里化。
function foo(a: number, b: number, c: number) {
return a + b + c
}
function bar(a: number) {
return function(b: number) {
return function(c: number) {
return a + b + c
}
}
}
foo(1, 2, 3) // 输出 6
bar(1)(2)(3) // 输出 6
function foo(a, b, c) {
return a + b + c
}
function bar(a) {
return function(b) {
return function(c) {
return a + b + c
}
}
}
foo(1, 2, 3) // 输出 6
bar(1)(2)(3) // 输出 6
优点
职责单一化
对函数的逻辑解构,一个函数只负责一个功能,经处理的参数,传入下一个函数,如下所示。
function foo(a: number, b: number, c: number) {
a = a + 1
return function(b: number, c: number) {
b = b * 2
return function(c: number) {
c = c - 1
return a + b + c
}
}
}
function foo(a, b, c) {
a = a + 1
return function(b, c) {
b = b * 2
return function(c) {
c = c - 1
return a + b + c
}
}
}
逻辑可复用
如下所示,有一个加法函数 add
。假如需要实现一个加 3 的函数,则必须每次都传入 3 以及另一个参数。
function add(a: number, b: number) {
return a + b
}
add(3, 1)
add(3, 2)
function add(a, b) {
return a + b
}
add(3, 1)
add(3, 2)
对其柯里化后,add
函数不再需要每次传入 3,该变量会保存在外层函数中。
function add(a: number, b: number) {
return function(b: number) {
return a + b
}
}
const add3 = add(3)
add3(1)
add3(2)
function add(a, b) {
return function(b) {
return a + b
}
}
const add3 = add(3)
add3(1)
add3(2)
实现
- 从以上的例子可以看出,柯里化函数实际是以闭包实现的。不同的是,若要实现自动柯里化,需要在外层函数接受原函数,内层函数接受参数。
- 由于原函数
fn
的参数不确定,因此需要递归以接受参数,直到传入参数长度等于原函数的参数长度,才会开始执行原函数fn
。
type fnType<T> = (...args: T[]) => any
/**
* 柯里化
* @param fn 需要柯里化的函数
* @returns 柯里化的函数
*/
function currying<T>(fn: fnType<T>) {
const curried = function(...args: T[]) {
if (args.length >= fn.length) {
return fn.apply(this, args)
} else {
const _curried = function(...otherArgs: T[]) {
return curried.apply(this, args.concat(otherArgs))
}
return _curried
}
}
return curried
}
/**
* 柯里化
* @param {Function} fn 需要柯里化的函数
* @returns 柯里化的函数
*/
function currying(fn) {
const curried = function(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args)
} else {
const _curried = function(...otherArgs) {
return curried.apply(this, args.concat(otherArgs))
}
return _curried
}
}
return curried
}