美文网首页工作生活
柯里化函数与反柯里化函数

柯里化函数与反柯里化函数

作者: Rudy_Ran | 来源:发表于2019-08-01 17:14 被阅读0次

    什么是柯里化(currying)

    柯里化是一个将多元函数转化为低元函数的操作过程,可以看做是多元函数的部分求值过程,一个柯里化的函数可能会接受一些参数,但在接受这些参数后,并不会立即运算求值,而是返回另外一个函数,传入的参数在函数中通过闭包的方式保存起来,等到需要求值的时候,在一次性运算求值。

    举例

    现在假设你是一个小超市的收银员,在每天工作结束后,你都要记录今天的收银账目,并且在每个月的月底跟老板汇报,这个月的收银总数。

    你可以采用如下方式:

    • 每天记录当日收银,并且每天都计算一次收银总数
    • 每天记录当日收银,在月底的时候一起算出总数

    方式一

    var sum = 0;
    var count = function(daycount){
         sum += daycount
    }
    count(100) //第1天
    count(200) //第2天
    count(300)
    // ...
    count(200) // 第30天
    console.log(sum )
    

    方式二

    function count (){
        var args = [];
        return function(){
            //如果没有参数,则开始计算
            if(arguments.length === 0){
                var sum = 0;
                for(var i=0;i<args.length;i++){
                    sum += args[i]
                }
                return sum
            }else{
                //如果有参数,暂存到args中
                [].push.apply(args,arguments);
            }
        }
    };
    
    count(100); // args --- [100]
    count(200); // args --- [100,200]
    count(300); // args --- [100,200,300]
    console.log(count()); // sum --- 600
    

    方法二的代码就是一个currying函数,当传入参数时,并没有执行,只是进行暂存的操作,只有通过count() 方式执行时,才会真正运算。

    通用的柯里化函数

    var currying = function (fn) {
        var args = [];
        return function () {
          if (arguments.length === 0) {
            return fn.apply(this, args);
          } else {
            [].push.apply(args, arguments);
                    //callee属性指代当前正在执行的函数
            return arguments.callee;
          }
        }
      };
      var count= (function () {
        var sum= 0;
        return function () {
          for (var i = 0; i < arguments.length; i++) {
            sum+= arguments[i];
          }
          return sum;
        }
      })();
      var count= currying(count); // 转化成 currying 函数
      count(100); 
      count(200); 
      count(300);  
      console.log(count());  // 求值并输出:600
    

    上面就是一个通用的currying函数,现在这个currying函数只能接收一个参数 fn ,但有时我们可能不仅仅只传一个需要柯里化的函数,还要传一些额外的参数,这就需要对上面的函数进行改写

    可传参的柯里化函数

    var currying = function (fn) {
        var args = [];
        /*将除了第一个参数fn之外的参数,存储到args中
                arguments本身是没有slice方法的,因此想取到arguments中除了第一项以外的参数
          就要通过[].slice.call(arguments,1) 相当于把数组slice的方法,搬到arguments上
            */
          args = args.concat([].slice.call(arguments,1)); 
        return function () {
          if (arguments.length === 0) {
            return fn.apply(this, args);
          } else {
            [].push.apply(args, arguments);
                    //callee属性指代当前正在执行的函数
            return arguments.callee;
          }
        }
      };
    

    什么是反柯里化(uncurrying)

    顾名思义,反柯里化的意义和柯里化相反,柯里化是一个逐步传参,逐步缩小函数的使用范围的过程

    而反柯里化作用在于扩大函数的使用性,将本来作为特性对象所拥有的功能函数变成可以被任意对象所使用

    比如:

    obj.func(arg1,arg2)
    

    变成

    func(obj,arg1,arg2)
    

    就是一个反柯里化的过程

    简单实现

    Function.prototype.uncurrying = function() {
        var thiz= this;
        return function() {
            return Function.prototype.call.apply(thiz,arguments);
        }
    };
    
    function sayHi () {
        return "Hello " + this.value +" "+[].slice.call(arguments);
    }
    var sayHiuncurrying=sayHi.uncurrying();
    console.log(sayHiuncurrying({value:'world'},"hahaha"));
    

    因为uncurrying是定义在Functionprototype上的方法,因此所有的函数都可以使用改方法。

    在调用的时候,uncurrying中的this指向sayHi这个函数,在调用sayHiuncurrying(arg1, arg2, ...)相当于调用sayHi.call(arg1, arg2, ...)

    sayHi.call(arg1, arg2, ...), call 函数把 arg1 当做 sayHi的上下文,然后把 arg2,... 等剩下的参数传给sayHi,因此最后相当于 arg1.sayHi(arg2,...);

    因此,这相当于 sayHiuncurrying(obj,args) 等于 obj.sayHi(args)

    通用的反柯里化函数

    var uncurrying= function (fn) {
        return function () {
            var args=[].slice.call(arguments,1);
            return fn.apply(arguments[0],args);        
        }    
    };

    相关文章

      网友评论

        本文标题:柯里化函数与反柯里化函数

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