美文网首页
函数柯里化

函数柯里化

作者: 法西斯qwq | 来源:发表于2019-04-18 15:35 被阅读0次

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

     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

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

    1:按照curry的用法,原理是返回了一个函数,这个函数的参数个数不确定(你可能同时掏出两张一块钱),于是我们可以运用不定参数的写法:
     const curry = (fn) => {
      return (...args) =>{  //不定参数,想给多少给多少
        //给钱交货环节
    }
    }
    
    2:柯里化函数需要记住你已经给过他的参数,如果没给的话,则默认为一个空数组:
     const curry = (fn,arr=[]) => {  //arr数组用于记录已有参数
      return (...args) =>{   
        //给钱交货环节
      }
    }
    
    3:接下来每次调用的时候,需要检查参数是否给够,如果够了,则执行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函数。
    
    4:(优化方面)接下来就是要看看怎么写才能更简洁。

    首先,中间的代码可以写成一个立即执行函数,省掉一些...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

    相关文章

      网友评论

          本文标题:函数柯里化

          本文链接:https://www.haomeiwen.com/subject/anktgqtx.html