Javascript function challenge add(1,2) and add(1)(2) both should return 3

北城余情 提交于 2019-11-28 09:29:57

There is an article on Dr.Dobs Journal about "Currying and Partial Functions in JavaScript" which describes exactly this problem.

One solution found in this article is:

// a curried add
// accepts partial list of arguments
function add(x, y) {
     if (typeof y === "undefined") { // partial
        return function (y) {
              return x + y;
        };
     }
   // full application
   return x + y;
}

I wrote a function that generates a chain whose valueOf function and context (the this) is continually updated with the new sum, no matter how many arguments are passed each time.

/* add function */
function add() {
    "use strict";

    var args, sum, chain;

    args = Array.prototype.slice.call(arguments);
    sum = typeof this === 'number' ? this : 0;
    sum += args.reduce(function (p, n) { return p + n; }, 0);

    chain = add.bind(sum);

    chain.valueOf = function () {
        return sum;
    };

    return chain;
}

/* tests */
console.log('add(1, 2) = ' + add(1, 2));
console.log('add(1)(2) = ' + add(1)(2));
/* even cooler stuff */
console.log('add(1, 2)(3) = ' + add(1, 2)(3));
console.log('add(1, 2, 3)(4, 5)(6) = ' + add(1, 2, 3)(4, 5)(6));
/* retains expected state */
var add7 = add(7);
console.log('var add7 = add(7)');
console.log('add7(3) = ' + add7(3));
console.log('add7(8) = ' + add7(8));

The reason why both mechanisms are required is because the next call in the chain cannot access the binded function's custom valueOf, and any script attempting to evaluate the function as a number cannot access its context.

The only downside is the requirement of strict mode in order for this to remain a primitive.

Here's an edit to support both strict mode and non-strict mode:

function add() {
    var args, sum, chain;

    args = Array.prototype.slice.call(arguments);

    // Number object from non-strict mode
    if (this instanceof Number) {
        sum = Number(this);
    // number primitive from strict mode
    } else if (typeof this === 'number') {
        sum = this;
    // initial call to add
    } else {
        sum = 0;
    }

    sum += args.reduce(function (p, n) { return p + n; }, 0);

    chain = add.bind(sum);

    chain.valueOf = function () {
        return sum;
    };

    return chain;
}
function add(num1, num2){
    if (num1 && num2) {
        return num1 + num2;
    } else if (num1) {
        return function(num2){return num1 + num2;};
    }
    return 0;
}

The concept that you're looking for is called currying and it has to do with function transformation and partial function application. This is useful for when you find yourself calling the same function over and over with mostly the same arguments.

An example of implementing add(2)(6) via currying would look something like this...

function add(x,y) { 
  if (typeof y === 'undefined') {
    return function(y) {
      return x + y;
    }
  }
}

add(2)(4); // => 6

Additionally, you could do something like this...

var add6 = add(6);
typeof add6; // => 'function'
add6(4);     // => 10
var add = function(){
  // the function was called with 2 arguments
  if(arguments.length > 1)
    arguments.callee.first_argument = arguments[0];

  // if the first argument was initialized
  if(arguments.callee.first_argument){
    var result = arguments.callee.first_argument + arguments[arguments.length - 1];
    arguments.callee.first_argument = 0;

    return result;
  }else{// if the function was called with one argument only then we need to memorize it and return the same function handler 
    arguments.callee.first_argument = arguments.callee.first_argument || arguments[0];
    return arguments.callee;
  }
}
console.log(add(2)(4));
console.log(add(2, 4));

An extended solution which depends on the environment:

function add(){
  add.toString = function(){
    var answer = 0;
    for(i = 0; i < add.params.length; i++)
      answer += add.params[i];
    return answer;
  };

  add.params = add.params || [];
  for(var i = 0; i < arguments.length; i++)
    add.params.push(arguments[i])

  return add;
}

console.log(add(2)(4)(6)(8))
console.log(add(2, 4, 6, 8));

We can use the concept of closures which is provided by Javascript.

Code snippet:

function add(a,b){

    if(b !== undefined){

        console.log(a + b);
        return;

    }

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

}

add(2,3);
add(2)(3);

I decided to add another solution for fun, using some ES6 features:

/* add function */
const add = (function add () {
  const sum = [this, ...arguments].reduce((a, b) => a + b)
  const chain = add.bind(sum)

  chain[Symbol.toPrimitive] = hint => sum

  return chain
}).bind(0)

/* tests */
console.log(`add(1, 2) = ${add(1, 2)}`)
console.log(`add(1)(2) = ${add(1)(2)}`)
/* even cooler stuff */
console.log(`add(1, 2)(3) = ${add(1, 2)(3)}`)
console.log(`add(1, 2, 3)(4, 5)(6) = ${add(1, 2, 3)(4, 5)(6)}`)
/* retains expected state */
var add7 = add(7)
console.log('var add7 = add(7)')
console.log(`add7(3) = ${add7(3)}`)
console.log(`add7(8) = ${add7(8)}`)
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!