写点什么

为什么我要说:柯里化 == 闭包 + 递归?

作者:掘金安东尼
  • 2022 年 8 月 07 日
  • 本文字数:1831 字

    阅读完需:约 6 分钟

为什么我要说:柯里化 == 闭包+递归?

柯里化是 JS 高程中不可或缺的重心,本篇带你来冲一冲它!!


我们不妨以两数相加为例子,递进说明。


我们通常是这样写一个函数来求得 两数相加 的值:


function sum(a,b){    console.log(a+b)}sum(1,2)
复制代码


这样写一点毛病没有!


不过呢?问题总会在发展中产生,产品经理又要加一个值,需求:三数相加


咱通常来说,第一时间,就在原基础上,直接再加一个参数就是了;


于是,修改后像是这样:


function sum(a,b,c){    console.log(a+b+c)}sum(1,2,3)
复制代码


问:这样写,有毛病吗??


答:太有毛病了!


这样一改,既违反了:“开闭原则”、又违反了:“单一职责原则”。


为不太熟悉设计原则的小伙伴们,简单解释下:


  • 什么是“开闭原则”?即:我们编程中要尽可能的避免直接修改函数、类或模块,而是要在原有基础上拓展它;

  • 什么是“单一职责原则”?即:每个函数、类或模块,应该只负责一个单一的功能;


首先,咱修改了 sum 函数的传参以及内部的调用 ⇒ 则违反“开闭原则”


其次,sum 函数本来只负责两数相加,修改后,它又负责三数相加,职责已经发生了变化 ⇒ 则违反 “单一职责原则”;


如果正规按照单一责任来写,应该是:


// 负责两数相加function sum2(a,b){    console.log(a+b)}
// 负责三数相加function sum3(a,b,c){ console.log(a+b+c)}
复制代码


事实上,是不可能这样去写的,因为如果有一万个数相加,得写一万个函数。


加法只有一个!! 不管你最终要加几个值,总是要一个加一个。


于是乎,我们设想,能不能写一个这样的函数:它的功能,就是“加”,参数跟几个,我就加几个。


// 负责“加法”,function addCurry(){    ...    ...    ...}
addCurry(1)(2) // 两数相加addCurry(1)(2)(3) // 三数相加...addCurry(1)(2)(3)...(n) // n 数相加
复制代码


没错,这个函数就是:柯里化!!(或者说这个过程叫柯里化,这个思想叫柯里化,本瓜认为这里不需要太死扣定义)


接着,我们一步步来试试,它会是怎样构成的?


为了能够实现一个加一个,即存储参数的目的,我们想一想,还有什么法宝?


没错,JS 奥义:闭包!


其实,本瓜时常想,闭包的终极秘密是什么?最后将其理解为 4 个金光闪闪的大字:延迟处理


什么意思?简单解释下:


function directHandle(a,b){    console.log("直接处理",a,b)}
directHandle(111,222)
// 直接处理 111 222
function delayHandle(a){ return function(b){ console.log("延迟处理",a,b) }}
delayHandle(111)
// ƒ (b){// console.log("延迟处理",a,b)// }
复制代码


如上 delayHandle(111) 不像 directHandle(111,222) 直接打印值,而是先返回一个函数 f(b);111 也被临时保存了,delayHandle(111)(222),则得到相同的输出。这就是:延迟处理的思想。


另外补一句:延迟处理是函数式编程的精华所在,在不能保证每个函数都是纯函数的前提下,在管道处理的最后,再进行处理,能最大程度的保证减少副作用。也就是 Monad 思想,此处不做展开。


言归正传,于是乎,我们借用闭包来实现最初版的柯里化:


// 两数相加function addCurry(a){    return function(b){            console.log(a+b)    }}
addCurry(1)(2)
// 三数相加function addCurry(a){ return function(b){ return function(c){ console.log(a+b+c) } }}
addCurry(1)(2)(3)
复制代码


写两个闭包的过程,聪明的你一定就明白了,这样一直写下去,不就是递归吗?!


于是乎,我们知道,当参数是 n 个的时候,需要递归 n-1 次 return function


于是乎,addCurry 写法如下:



let arr = [] function addCurry() { let arg = Array.prototype.slice.call(arguments); // 递归获取后续参数 arr = arr.concat(arg); if (arg.length === 0) { // 如果参数为空,则判断递归结束 return arr.reduce((a,b)=>{return a+b}) // 求和 } else { return addCurry; } }
addCurry(1)(2)(3)()
复制代码


OK,至此,,大功告成!!


以上,用最简单的代码解释了 —— 为什么我说:柯里化 == 闭包+递归 ?


柯里化是一种思想,上面的 addCurry 可以说是最简单的一种实践。在函数式编程中,Curry 更是大放异彩,比如 compose(fn1)(fn2)(fn3)…(fnN)(args) 等等。


如果以后有人再问你柯里化,可以往这个方向上答。。。


OK,以上便是本篇分享。 觉得不错点个赞吧👍👍👍,您的鼓励,我的动力,坚持原创质量好文~~ 欢迎评论留言 我是掘金安东尼,输出暴露输入,技术洞见生活。再会吧~~ 👋👋👋

发布于: 刚刚阅读数: 4
用户头像

安东尼陪你度过漫长编程岁月~ 2022.07.14 加入

社会我瓜哥,人狠话不多😎 微信 anthony1453,加我交个朋友😎 正联合【机械工业出版社】出版《程序员成长手册》,敬请期待😎 真正的大师,永远怀着一颗学徒的心(易)😎

评论

发布
暂无评论
为什么我要说:柯里化 == 闭包+递归?_JavaScript_掘金安东尼_InfoQ写作社区