函数柯里化 currying
- 函数柯里化 currying
- 偏函数 partial
- 反柯里化 uncurrying
- 组合函数 compose
概念
柯里化(currying)又称部分求值。
柯里化函数(Currying Function),是一种函数式编程技术。
它把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。
核心思想
柯里化函数的核心思想是函数的“延迟执行”,它能够将一个函数的参数逐个收集起来,直到所有的参数都收集齐了才开始执行函数。柯里化函数使得函数的可组合性更强,同时也支持给部分参数赋值的情况下调用函数。这种方式能够使得代码更加灵活,易于重用。
js
// 实现方法:用一个闭包,返回一个函数,
// 这个函数每次执行都会改写储存参数的数组,
// 当函数的参数够了之后,便会执行。
function add(a, b, c) {
return a + b + c
}
add(1, 2, 3)
let addCurry = currying(add)
addCurry(1)(2)(3)
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
实现
js
// 函数柯里化
function currying(fn, ...rest) {
// 判断参数的长度是否已经满足函数所需参数的长度
// 如果满足,执行函数
// 如果不满足,递归返回科里化的函数,等待参数的传入
return fn.length <= rest.length
? fn(...rest)
: currying.bind(null, fn, ...rest)
// 等效
// if (fn.length <= rest.length) {
// return fn(...rest)
// } else {
// return function (...rest2) {
// return currying(fn, ...rest, ...rest2)
// }
// }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
js
// testing
function sumFn(a, b, c) {
return a + b + c
}
let sum = currying(sumFn)
console.log(sum(2)(3)(5)) // 10
console.log(sum(2, 3)(5)) // 10
// 乘法
const mul = (x) => (y) => (z) => x * y * z
console.log(mul(1)(2)(3))
const mul1 = (x, y, z) => x * y * z
const mul2 = currying(mul1)
console.log(mul2(1)(2)(3))
console.log(mul2(1, 2)(3))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
优点
更加灵活的函数组合
js
const add = (x) => (y) => x + y
const multiply = (x) => (y) => x * y
const addAndMultiply = (x) => add(2)(multiply(3)(x))
addAndMultiply(4) // 14
1
2
3
4
5
6
2
3
4
5
6
支持延迟执行
柯里化函数支持延迟执行,因为它们只会执行到所有参数都被收集完毕才会执行。这种方式能够提高函数的性能和可复用性。
js
function add(x, y) {
return x + y
}
function currying(fn) {
return function curried() {
const args = Array.from(arguments)
if (args.length >= fn.length) {
return fn.apply(null, args)
} else {
return function () {
const newArgs = Array.from(arguments)
return curried.apply(null, args.concat(newArgs))
}
}
}
}
const lazyAdd = currying(add)
const result1 = lazyAdd(1)(2) // 不执行
const result2 = lazyAdd(1, 2) // 执行
console.log(result1) // 返回一个函数
console.log(result2) // 返回结果 3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
上面的例子中,我们使用柯里化函数 lazyAdd
对 add
函数进行了包装,使其能够支持延迟执行。当我们传递一个参数时,它不会马上执行 add
函数,而是返回一个新函数去等待接收下一个参数。直到满足执行条件后再一次性执行。而当我们传递足够的参数时,它会立即执行 add
函数并返回结果。
偏函数 partial
偏函数(Partial Application)
偏函数是指使用柯里化函数对函数的一部分参数进行预处理,并返回一个函数来完成余下的工作。
就是将一个 n 参的函数转换成固定 x 参的函数,剩余参数(n - x)将在下次调用全部传入。
举个例子:
js
// 计算税率
function calculateTax(rate, price) {
return rate * price
}
// 创建一个新的函数来计算 5% 的税收 (偏函数)
const calculate5PercentTax = currying(calculateTax, 0.05)
console.log(calculate5PercentTax(100)) // 5
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
反柯里化 uncurrying
反柯里化是指将柯里化函数还原成原函数的形式,使其能够接收多个参数。
js
const add = (x) => (y) => x + y
function uncurrying(fn) {
return function () {
const args = Array.from(arguments)
return args.reduce((pre, cur) => pre(cur), fn)
}
}
const add2 = uncurrying(add)
console.log(add2(1, 2)) // 3
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
组合函数 compose
组合函数是指将多个函数组合到一起以达到某种效果的函数。
js
function double(x) {
return x * 2
}
function add(x, y) {
return x + y
}
const compose = (...fns) => {
return function (result) {
return fns.reduceRight((result, fn) => fn(result), result)
}
}
const doubleAndAdd5 = compose(add(5), double)
doubleAndAdd5(2) // 9
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
惰性求值(Lazy Evaluation)
惰性求值是指在需要的时候才进行计算,这种方式能够提高程序的性能和响应速度。
js
function add(x, y) {
return x + y
}
// 使用惰性求值的方式将其转化成柯里化函数
// 在真正需要计算结果的时候再进行计算,从而避免不必要的计算,提高程序的性能。
const add = (x) => {
return function (y) {
if (y !== undefined) {
return x + y
} else {
return function (y) {
return x + y
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
参考: