资讯专栏INFORMATION COLUMN

一道题看透函数柯里化

marser / 3290人阅读

摘要:对于函数的柯里化应该不陌生,简单来说技术是一种通过把多个参数填充到函数体中,实现将函数转换为一个新的经过简化的使之接受的参数更少函数的技术。

对于函数的柯里化(currying)应该不陌生,简单来说 Currying 技术是一种通过把多个参数填充到函数体中,实现将函数转换为一个新的经过简化的(使之接受的参数更少)函数的技术。当发现正在调用同一个函数时,并且传递的参数绝大多数都是相同的,那么用一个Curry化的函数是一个很好的选择.

下面利用闭包实现一个curry化的加法函数, 我们简单理解一下 curry 化:

function add(x, y){
    if(x && y) return x + y;
    if(!x && !y) throw Error("Cannot calculate");
    return function(newx){
        return x + newx;
    };
}
add(3)(4); //7
add(3, 4); //7
var newAdd = add(5);
newAdd(8); //13
var add2000 = add(2000);
add2000(100); //2100

这样做其实很类似 bind:

function add(a, b){
  console.log(a+b);
  return a + b;
}

add(3, 4);   //7
add.bind(null, 3)(4);   //7
var newAdd = add.bind(null, 5);
newAdd(8);   //13
var add2000 = add.bind(null, 2000);
add2000(100); //2100

同理也可以使用 call 和 apply, 因为他们可以实现 bind 的功能:

Function.prototype.bind = function(context){
  var _this = this;
  var args = [].slice.call(arguments, 1);

  return function (){
    innerArgs = [].slice.call(arguments);
    if(innerArgs && innerArgs.length > 0)
      args.push.apply(args, innerArgs);
    return _this.apply(context, args);
  }
}
add(3, 4);   //7
add.bind(null, 3)(4);   //7
var newAdd = add.bind(null, 5);
newAdd(8);   //13
var add2000 = add.bind(null, 2000);
add2000(100); //2100

但是,如果看到了这个题:

实现一个函数sum,运算结果可以满足如下预期结果:
sum(1,2,3);       //6
sum(2,3)(2);      //7
sum(1)(2)(3)(4);  //10
sum(2)(4,1)(2);   //9

还觉得简单么?我们理一下思路。首先试想一下这个 sum 函数的结构:

function sum(){
  return function(){
    return function(){
      //...
    }
  }
}

这个函数返回的一定是个函数,但貌似需要写无限个,这个不合理,我们修改一下:

function sum(){
  function innerSum(){
    //...
    return innerSum();
  }
  return innerSum();
}

这样一来每次调用就不需要定义无限个函数了。我们完善里面的代码:

//sum(1,2,3);       //6
//sum(2,3)(2);      //7
//sum(1)(2)(3)(4);  //10
//sum(2)(4,1)(2);   //9
function sum(){
  var cur = [].slice.call(arguments).reduce(function(a,b){return a+b;},0);
  function innerSum(){
    var next = [].slice.call(arguments).reduce(function(a,b){return a+b;},0);
    cur += next;
    return innerSum;
  }
  return innerSum;
}

这样 sum 函数的柯里化过程就完成了,但是这个函数的返回的总是一个函数,这样我们如何输出数值呢?我们可以借助隐式类型转换需要的 toString 函数实现:

function sum(){
  var cur = [].slice.call(arguments).reduce(function(a,b){return a+b;},0);
  function innerSum(){
    var next = [].slice.call(arguments).reduce(function(a,b){return a+b;},0);
    cur += next;
    return innerSum;
  }
  innerSum.toString = function(){
    return cur;
  }
  return innerSum;
}
console.log(sum(1,2,3));       //6
console.log(sum(2,3)(2));      //7
console.log(sum(1)(2)(3)(4));  //10
console.log(sum(2)(4,1)(2));   //9

计算结果没错,我们还可以换作 valueOf 实现:

function sum(){
  var cur = [].slice.call(arguments).reduce(function(a,b){return a+b;},0);
  function innerSum(){
    var next = [].slice.call(arguments).reduce(function(a,b){return a+b;},0);
    cur += next;
    return innerSum;
  }
  innerSum.valueOf = function(){
    return cur;
  }
  return innerSum;
}
console.log(sum(1,2,3));       //6
console.log(sum(2,3)(2));      //7
console.log(sum(1)(2)(3)(4));  //10
console.log(sum(2)(4,1)(2));   //9

其实,如果同时存在 toString 和 valueOf 系统会先调用 toString, 然后调用valueOf,返回值自然是 valueOf 的返回值。这个很基础,这里就不提了。

通用柯里化方法

通用的柯里化写法其实比之前的 sum 函数要简单许多

var currying = function(fn) {
  // 主要还是收集所有需要的参数到一个数组中,便于统一计算
  var args = [].slice.call(arguments, 1);
  return function(){
      var _args = args.concat([].slice.call(arguments));
      return fn.apply(null, _args);
  }
}

var sum = function(){
  var args = [].slice.call(arguments);
  return args.reduce(function(a, b) {
      return a + b;
  });
};

var sum10 = currying(sum, 10);
console.log(sum10(20, 10));  // 40
console.log(sum10(10, 5));   // 25

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/107628.html

相关文章

  • 一道面试谈谈函数柯里(Currying)

    摘要:忍者秘籍一书中,对于柯里化的定义如下在一个函数中首先填充几个参数然后再返回一个新函数的技术称为柯里化。回到我们的题目本身,其实根据测试用例我们可以发现,函数的要求就是接受单一函数,例如但是与柯里化不同之处在于,柯里化返回的一个新函数。   欢迎大家再一次来到我的文章专栏:从面试题中我们能学到什么,各位同行小伙伴是否已经开始了悠闲的春节假期呢?在这里提前祝大家鸡年大吉吧~哈哈,之前有人说...

    cppprimer 评论0 收藏0
  • 一道面试认识函数柯里

    摘要:函数柯里化在函数式编程中,函数是一等公民。函数柯里化的主要作用和特点就是参数复用提前返回和延迟执行。可能在实际应用场景中,很少使用函数柯里化的解决方案,但是了解认识函数柯里化对自身的提升还是有帮助的。 最近在整理面试资源的时候,发现一道有意思的题目,所以就记录下来。 题目 如何实现 multi(2)(3)(4)=24? 首先来分析下这道题,实现一个 multi 函数并依次传入参数执行,...

    13651657101 评论0 收藏0
  • 一道柯里面试

    摘要:这是一道朋友在群里发的一道题,我之前不是很懂柯里化,就自己试着写了一下,不知道算不算柯里化,望指教下面是题目写好之后一下代码可以正常运行输入正确我自己的代码我用到了以下知识点扩展运算符传参和扩展运算符相关的数组操作。 这是一道朋友在群里发的一道题,我之前不是很懂柯里化,就自己试着写了一下,不知道算不算柯里化,望指教~ 下面是题目: function curry() { ...

    andycall 评论0 收藏0
  • 关于JavaScript函数柯里探索

    摘要:函数柯里化关于函数柯里化的问题最初是在忍者秘籍中讲闭包的部分中看到的,相信很多同学见过这样一道和柯里化有关的面试题实现一个函数,使得如下断言能够能够通过简单说就是实现一个求值函数,能够将所有参数相加得出结果。方法返回一个表示该对象的字符串。 函数柯里化   关于函数柯里化的问题最初是在《JavaScript忍者秘籍》中讲闭包的部分中看到的,相信很多同学见过这样一道和柯里化有关的面试题:...

    vboy1010 评论0 收藏0
  • 「前端面试系列6」理解函数柯里

    摘要:原题如下写一个方法,当使用下面的语法调用时,能正常工作这道题要考察的,就是对函数柯里化的理解。当参数只有一个的时候,进行柯里化的处理。这其实就是函数柯里化的简单应用。 showImg(https://segmentfault.com/img/bVbopGm?w=620&h=350); 前言 这是前端面试题系列的第 6 篇,你可能错过了前面的篇章,可以在这里找到: ES6 中箭头函数的...

    liaorio 评论0 收藏0

发表评论

0条评论

marser

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<