ES6如何用一句代码实现函数的柯里化


Posted in Javascript onJanuary 18, 2020

柯里化是干什么的?首先看看下面这个函数

let store = (a,b,c) => "这是你的七仔面" 
//函数就好像一个小卖部,一碗七仔面要三张软妹币:a,b,c (五个参数)

那函数柯里化是什么?就是我们买面吃的过程可能是这样:

let curryStore = curry(store) //刚刚的小卖部被柯里化了

//最佳状况
curryStore(1,5,1)// 老板,刚好!不用找了,刚好七块钱。老板: "这是你的七仔面" 

//偶尔出现的情况
let boss = curryStore(5) //老板,这五块钱你先拿着,我找找有没有一块钱。老板:……
boss = boss(1) //有了老板,这一块钱你先拿着,我找找还有没有一块钱。。老板:……
boss(1) //哈哈,终于找到了,给!老板:"这是你的七仔面" 

curryStore(5)(1)(1) //等价于上诉情况

所以这里我们可以看到,函数柯里化,是可以用来慢慢凑齐参数,延迟函数的执行。(先分期交钱,后交货!)

做个题

现在,我们的目标是实现一个curry 函数,达到以下使用效果:
在给到足够的参数时,执行函数。不够参数的时候则返回一个新的curry函数。

let curryPlus = curry((a,b,c) => a+b+c) //这里给到一个有三个参数的函数

curryPlus(1)(2)(3) //返回 6
curryPlus(1)(2,3) //返回 6
curryPlus(1,2)(3) //返回 6

let x = curryPlus(1)(2) //喂,怎么才两个参数?返回一个curry函数(已经带了两个参数)
x(1) //返回 4
x(2) //返回 5

作为一个js很厉害的人,这种问题用一行代码解决不是问题,问题是我没那么厉害。。。

于是,我们先从土方法说起吧orz

按照curry的用法,原理是返回了一个函数,这个函数的参数个数不确定(你可能同时掏出两张一块钱),于是我们可以运用不定参数的写法:

const curry = (fn) => {
  return (...args) =>{ //不定参数,想给多少给多少
    //给钱交货环节
  }
}

柯里化函数需要记住你已经给过他的参数,如果没给的话,则默认为一个空数组:

const curry = (fn,arr=[]) => { //arr数组用于记录已有参数
  return (...args) =>{  
    //给钱交货环节
  }
}

接下来每次调用的时候,需要检查参数是否给够,如果够了,则执行fn,没有的话则返回一个新的curry函数,将现有的参数塞给他:

const curry = ( fn, arr = []) => {
  return (...args) => { 

    //判断参数总数是否和fn参数个数相等
    if([...arr, ...args].length === fn.length){
      return fn(...arr, ...args) //拓展参数,调用fn
    }else{
      return curry(fn,[...arr, ...args]) //迭代,传入现有的所有参数
    }

  }
}

到这里,其实我们已经实现了curry函数。

接下来就是要看看怎么写才能更简洁。
首先,中间的代码可以写成一个立即执行函数,省掉一些...arr, ...args:

const curry = ( fn, arr = []) => {
  return (...args) => { 

    return ( a => {  //a是一个数组
      if(a.length === fn.length) {
        return fn(...a)
      }else{
        return curry(fn, a)
      }
    })([...arr, ...args]) //这里把arr和args摊开成一个数组赋值给a

  }
}

if语句可以缩减成三元表达式,也可以省掉很多字啦:

const curry = ( fn, arr = []) => {
  return (...args) => { 

    return ( a => { 

      return a.length === fn.length ? fn(...a) : curry(fn, a)

    })([...arr, ...args]) 

  }
}

最后,既然函数里啥事都没干,就只是return的话,不妨使用箭头函数最省的写法 input => output, 把return和大括号都给省了:

const curry = ( fn, arr = []) => {
  return (...args) => { 
    return ( a => a.length === fn.length? fn(...a) : curry(fn, a))([...arr, ...args])  //先折叠一层
  }
}

再折叠:

const curry = ( fn, arr = []) => {
  return (...args) => ( a => a.length === fn.length? fn(...a) : curry(fn, a))([...arr, ...args])  //再折叠
}

收工了收工了:

const curry = ( fn, arr = []) => (...args) => ( a => a.length === fn.length? fn(...a) : curry(fn, a))([...arr, ...args]) 
//衣服给您叠好了

拿去试试:

const curry = ( fn, arr = []) => (...args) => ( a => a.length === fn.length? fn(...a) : curry(fn, a))([...arr, ...args])
let curryPlus = curry((a,b,c,d)=>a+b+c+d)

curryPlus(1,2,3)(4) //返回10
curryPlus(1,2)(4)(3) //返回10
curryPlus(1,2)(3,4) //返回10

当然了,柯里化函数的主要作用还是延迟执行,执行的触发条件不一定是参数个数相等,也可以是其他的条件,例如参数个为0的情况,那么我们需要对上面curry函数稍微做修改:

const curry = ( fn, arr = []) => (...args) => ( (a,b) => b.length === 0? fn(...a) : curry(fn, a))([...arr, ...args],[...args])
let curryPlus = curry((...x)=>x.reduce((a,b)=>a+b))

curryPlus(1) //返回一个函数
curryPlus(1)(2) //返回一个函数

//遇到参数个数为0的情况才执行
curryPlus(1)(2)(4)() //返回7
curryPlus(1,2)(4)() //返回7

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
ASP 过滤数组重复数据函数(加强版)
May 31 Javascript
html+css+js实现xp window界面及有关功能
Mar 26 Javascript
jQuery修改li下的样式以及li下的img的src的值的方法
Nov 02 Javascript
jQuery中slice()方法用法实例
Jan 07 Javascript
js仿支付宝填写支付密码效果实现多方框输入密码
Mar 09 Javascript
dul无法加载bootstrap实现unload table/user恢复
Sep 29 Javascript
快速实现JS图片懒加载(可视区域加载)示例代码
Jan 04 Javascript
AngularJS的依赖注入实例分析(使用module和injector)
Jan 19 Javascript
ReactJs实现树形结构的数据显示的组件的示例
Aug 18 Javascript
详解JavaScript中关于this指向的4种情况
Apr 18 Javascript
微信小程序动态显示项目倒计时
Jun 20 Javascript
JS document内容及样式操作完整示例
Jan 14 Javascript
ES6 Object.assign()的用法及其使用
Jan 18 #Javascript
vue项目中监听手机物理返回键的实现
Jan 18 #Javascript
vue组件内部引入外部js文件的方法
Jan 18 #Javascript
Node.js实现批量下载图片简单操作示例
Jan 18 #Javascript
vue实现微信浏览器左上角返回按钮拦截功能
Jan 18 #Javascript
vue+elementUi 实现密码显示/隐藏+小图标变化功能
Jan 18 #Javascript
JS数组方法slice()用法实例分析
Jan 18 #Javascript
You might like
收音机频率指针指示不准确和灵敏度低问题
2021/03/02 无线电
建立动态的WML站点(二)
2006/10/09 PHP
php4的session功能评述(三)
2006/10/09 PHP
使用php转义输出HTML到JavaScript
2015/03/27 PHP
Laravel最佳分割路由文件(routes.php)的方式
2016/08/04 PHP
javascript实现的网页局布刷新效果
2008/12/01 Javascript
Extjs Ajax 乱码问题解决方案
2009/04/15 Javascript
EasyUi tabs的高度与宽度根据IE窗口的变化自适应代码
2010/10/26 Javascript
jQuery 阴影插件代码分享
2012/01/09 Javascript
使用javascipt---实现二分查找法
2013/04/10 Javascript
js获取url参数代码实例分享(JS操作URL)
2013/12/13 Javascript
jQuery切换所有复选框选中状态的方法
2015/07/02 Javascript
浅谈Javascript数组索引
2015/07/29 Javascript
js获取本机操作系统类型的两种方法
2015/12/19 Javascript
jquery删除table当前行的实例代码
2016/10/07 Javascript
Vue.js系列之vue-router(上)(3)
2017/01/03 Javascript
JavaScript实现的select点菜功能示例
2017/01/16 Javascript
JS替换字符串中指定位置的字符(多种方法)
2020/05/28 Javascript
vue实践---根据不同环境,自动转换请求的url地址操作
2020/09/21 Javascript
[00:33]2016完美“圣”典风云人物:BurNIng宣传片
2016/12/10 DOTA
[03:48]DOTA2完美大师赛主赛事第二日精彩集锦
2017/11/24 DOTA
[14:50]2018DOTA2亚洲邀请赛开幕式
2018/04/03 DOTA
Python模仿POST提交HTTP数据及使用Cookie值的方法
2014/11/10 Python
python递归删除指定目录及其所有内容的方法
2017/01/13 Python
Python实现爬虫设置代理IP和伪装成浏览器的方法分享
2018/05/07 Python
学Python 3的理由和必要性
2019/11/19 Python
tensorflow使用指定gpu的方法
2020/02/04 Python
简单了解django处理跨域请求最佳解决方案
2020/03/25 Python
法国二手手袋、手表和奢侈珠宝购物网站:Collector Square
2018/07/05 全球购物
乌克兰鞋类购物网站:Eobuv.com.ua
2020/11/28 全球购物
企业文化口号
2014/06/12 职场文书
优秀少先队员事迹材料
2014/12/24 职场文书
Html5大屏数据可视化开发的实现
2021/06/11 HTML / CSS
Win11怎么启动任务管理器?Win11启动任务管理器的几种方法
2021/11/23 数码科技
「地球外少年少女」BD发售宣传CM公开
2022/03/21 日漫
教你使用Python获取QQ音乐某个歌手的歌单
2022/04/03 Python