柯里化和反柯里化

2018-09-01  本文已影响0人  灯光树影

一、柯里化

/**
  牛顿第二定律
  @param m {number} 物体质量
  @param a {number} 加速度
*/
function F(m, a){
  return m * a;
}

现在有一个需求:就是物体的质量不变,但是它的加速度时刻改变,如果需要计算它某些时刻所受的力,那么就需要不断的调用F。
就像这样:

F(m, a0); // 0时刻
F(m, a1); // 1时刻
F(m, a2); // 2时刻 

可是,我们需要的质量是固定的,为什么不把函数的m固定,下一次调用只需要传入a的值就可以了。这时,我们希望函数调用变成这样:

F1(a0); // 0时刻
F1(a1); // 1时刻
F1(a2); // 2时刻 

这样,把部分参数固定的需求,生成一个新的函数(这里是F1)的过程就是函数柯里化。

/**
  函数柯里化
  @param func {function} 需要柯里化的函数(原函数)
  @return {function} 柯里化后的函数
*/
function currying(func){
   // 固定参数
   var slice = Array.prototype.slice;
   var args = slice.call(arguments, 1);
    /**
        --> slice.call(arguments, 1)
        --> arguments.slice(1);
    */

   // 返回新的函数
   return function(){
     // 获取新的函数调用时的参数 
     var inArgs = slice.call(arguments);
     // 把固定好的参数与inArgs拼接起来,调用原函数
     return func.apply(null, args.concat(inArgs)); 
   }
}

原理: 使用闭包保存需要固定的参数,返回一个“自动”将固定的参数值传递给原函数的新函数。

// 沿用上面的例子,生成F1函数
var m = 20; // 假设m等于20
var F1 = currying(F, m);
// 调用:
F1(a0);
F2(a1);
...

总结: 函数柯里化的目的就是把通用函数的适用范围缩小,使其专用性更强。

二、反柯里化

 var cat = {
   name: 'Tom',
   getName: function(){
     return this.name;
   }
 }
 var mouse = {
   name: 'Jerry'
 }

我们知道,输出cat的名字,使用cat.getName();只是这样,getName专用于cat对象,为了将它用于其它对象,比如mouse我希望得到一个这样的函数调用形式:

// obj是需要输出名字的对象
commonGetName(obj);

这个函数的内部应该实现类似这样的调用形式:

obj.getName();

这样,增加了不固定的参数,扩大了使用范围,就是函数反柯里化

/**
   函数反柯里化
   @return {function} 处理后的函数  
*/
function uncurring(){
    var that = this;
    return function(){
        return Function.prototype.call.apply(that, arguments);
        /**
           --> apply具有分解参数的作用,这很重要。
                 apply把arguments分解成了args1, args2, ...  
            --> that.call(args1, args2, args3, ...);
            --> args1.that(args2, args3, ...);
        */
    }
} 
commonGetName = cat.getName.uncurring();
commonGetName(cat); // 输出猫的名字
commonGetName(mouse); // 输出鼠的名字 

参考文章:
https://www.cnblogs.com/pigtail/p/3447660.html
https://www.imooc.com/article/46624

上一篇下一篇

猜你喜欢

热点阅读