什么是函数柯里化
在维基百科中,对函数柯里化有着这样的定义:
在计算机科学领域,柯里化(Currying)指的是将一个接受多个参数的函数转化为一个仅接受单一参数(即原函数的第一个参数)的函数,并且该函数会返回一个新函数,这个新函数能够接收剩余的参数并最终返回计算结果。
由此可见,函数柯里化是一种函数变形的思想,它本身并不会直接执行函数,函数柯里化的思想可在多种编程语言中实现,但实现方式因语言而异。上述解释已经十分通俗易懂了。简单来讲,当传入的参数数量不足时,柯里化会借助闭包将当前传入的参数保存起来,然后返回一个新函数。后续这个新函数可以继续被调用以获取更多参数,新传入的参数会与闭包中保存的参数合并。当参数数量达到被柯里化函数的形参数量时,函数就会开始执行;若参数数量仍然不足,则会继续返回新函数来保存参数,如此循环往复。下面以 JavaScript
为例进行说明:
function add(...nums) {
return nums.reduce((acc, num) => acc + num, 0);
}
// 对该函数进行柯里化处理后,就可以将多个参数拆分成单个或多个参数组合的形式传入,最终得到相同的结果。示例如下:
curryingAdd(1)(2)(3)(4); // 结果为 10
// 或者
curryingAdd(1, 2)(3)(4); // 结果为 10
// 又或者
curryingAdd(1)(2, 3, 4); // 结果为 10
柯里化的好处
乍看上述例子,你可能会觉得函数柯里化似乎没什么实际用途,反而让实现过程变得更加复杂。但实际上,它是一种高度抽象的编程规范,属于函数式编程思想的一部分,主要有以下优点:
参数复用
此外,柯里化还具备“提前确认”和“延迟执行”的特性。“提前确认”强调的是提前固定部分参数,明确函数的部分行为,例如在网络请求中提前确定请求方法;“延迟执行”强调的是函数不会立即执行,而是等所有必要参数都传入后才执行。它们虽然侧重点不同,但都体现了柯里化在灵活控制函数调用上的优势。在我看来,参数复用是函数柯里化最为显著的优势。合理运用这一特性,能够编写出更加优雅的代码,让程序的执行过程更贴合人类解决问题的思维方式。下面通过一段代码来进一步说明:
function ajax(method, url) {
// 此处为 HTTP 请求的具体实现代码...
console.log(`Sending ${method} request to ${url}`);
}
function currying(fn, ...args) {
if (args.length >= fn.length) {
return fn(...args);
} else {
return (...args2) => currying(fn, ...args, ...args2);
}
}
// 对 ajax 方法进行柯里化处理
let curryingAjax = currying(ajax);
// 得到一个专门用于处理 GET 请求的函数,该操作仅需执行一次
let getAjax = curryingAjax("get");
// 结合具体业务逻辑,获取用户信息的接口请求
let getUserInfo = getAjax("http://api.test.com/getUserInfo");
// 结合具体业务逻辑,获取订单列表的接口请求
let getOrderList = getAjax("http://api.test.com/getOrderList");
// 如果是 post 请求
let postAjax = curryingAjax("post");
let postData = postAjax("http://api.test.com/submitData");
从上述代码可以看出,get
方法仅需传入 curryingAjax
函数一次,后续就可以多次调用 get
请求。先确定请求方法,再确定具体的请求地址,这样就实现了参数的复用。
柯里化完整代码
柯里化函数 currying
的实现逻辑如下:
- 首先判断从
currying
函数的第二个参数开始到最后一个参数(即args
)的长度。若该长度等于被柯里化函数fn
的形参数量,那么直接执行fn
函数,并将args
作为参数传入。 - 若传入的参数(
args
)数量不足,就利用递归和闭包的特性,暂时保存已传入的参数,然后重新返回currying
函数。
function currying(fn, ...args) {
if (args.length >= fn.length) {
return fn(...args);
} else {
return (...args2) => currying(fn, ...args, ...args2);
}
}
// 使用示例
const add = (a, b, c) => {
console.log(a + b + c);
};
const currying_add = currying(add);
// 不同参数传入方式的结果展示
currying_add(1, 2, 3);
currying_add(1)(2, 3);
currying_add(1, 2)(3);
currying_add(1)(2)(3);