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 相关文章推荐
Jquery + Ajax调用webService实例代码(asp.net)
Aug 27 Javascript
js截取函数(indexOf,join等)
Sep 01 Javascript
JS图片浏览组件PhotoLook的公开属性方法介绍和进阶实例代码
Nov 09 Javascript
表单切换,用回车键替换Tab健(不支持IE)
Jul 20 Javascript
js控制web打印(局部打印)方法整理
May 29 Javascript
鼠标划过实现延迟加载并隐藏层的js代码
Oct 11 Javascript
jQuery中extend函数详解
Feb 13 Javascript
JavaScript每天定时更换皮肤样式的方法
Jul 01 Javascript
Ajax使用原生态JS验证用户名是否存在
May 26 Javascript
深入理解Vue生命周期、手动挂载及挂载子组件
Sep 27 Javascript
JS随机密码生成算法
Sep 23 Javascript
vue中根据时间戳判断对应的时间(今天 昨天 前天)
Dec 20 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
php设计模式 Prototype (原型模式)代码
2011/06/26 PHP
PHP模板引擎Smarty中变量的使用方法示例
2016/04/11 PHP
PHP简单实现上一页下一页功能示例
2016/09/14 PHP
Yii2下session跨域名共存的解决方案
2017/02/04 PHP
网站被恶意镜像怎么办 php一段代码轻松搞定(全面版)
2018/10/23 PHP
windows 2008r2+php5.6.28环境搭建详细过程
2019/06/18 PHP
PHP的静态方法与普通方法用法实例分析
2019/09/26 PHP
基于PHP实现邮箱验证激活过程详解
2020/10/28 PHP
看了就知道什么是JSON
2007/12/09 Javascript
javascript实现瀑布流自适应遇到的问题及解决方案
2015/01/28 Javascript
JavaScript将数字转换成大写中文的方法
2015/03/23 Javascript
JQuery实现简单的服务器轮询效果实例
2016/03/31 Javascript
基于JS实现密码框(password)中显示文字提示功能代码
2016/05/27 Javascript
关于JSON与JSONP简单总结
2016/08/16 Javascript
原生js实现类似fullpage的单页/全屏滚动
2017/01/22 Javascript
使用async-validator编写Form组件的方法
2018/01/10 Javascript
vue实现简单的日历效果
2020/09/24 Javascript
JQuery+Bootstrap 自定义全屏Loading插件的示例demo
2019/07/03 jQuery
js全屏事件fullscreenchange 实现全屏、退出全屏操作
2019/09/17 Javascript
vue如何搭建多页面多系统应用
2020/06/17 Javascript
python使用sorted函数对列表进行排序的方法
2015/04/04 Python
使用pycharm设置控制台不换行的操作方法
2019/01/19 Python
Tensorflow分类器项目自定义数据读入的实现
2019/02/05 Python
详解Django中CBV(Class Base Views)模型源码分析
2019/02/25 Python
numpy和pandas中数组的合并、拉直和重塑实例
2019/06/28 Python
关于windows下Tensorflow和pytorch安装教程
2020/02/04 Python
python如何随机生成高强度密码
2020/08/19 Python
Scrapy 配置动态代理IP的实现
2020/09/28 Python
Python列表嵌套常见坑点及解决方案
2020/09/30 Python
使用CSS3的font-face字体嵌入样式的方法讲解
2016/05/13 HTML / CSS
无需JS和jQuery代码实现CSS3鼠标浮动放大图片
2016/11/21 HTML / CSS
素质拓展感言
2014/01/29 职场文书
住房租房协议书
2014/08/20 职场文书
刑事代理授权委托书
2014/09/17 职场文书
2014年终个人工作总结
2014/11/07 职场文书
求职简历自我评价2015
2015/03/10 职场文书