Marcio
Marcio

Reputation: 622

Function calling 2 ways with multiple arguments

As most of us know we can create a simple function like this.

function calc(a,b){
   return a+b
}
calc(1,1); //returns 2

We can also make something like this

function calc(a){
    return function(b){
      return a+b
    }
}
calc(1)(1); //returns 2

What about if we had multiple arguments?

function calc() {
    function r(arg) {
        var a = [];
        for(var i = 0, l = arg.length; i < l; i++){
            a[i] = arg[i];
        }

        return a.reduce(function(p, c) {
          return p + c;
        });

    }
    var res = r(arguments);
    return function() {
        res += r(arguments);
        return res;
    }
}

This works for calc(1,2)(1) but it doesn't for calc(1,2,1)

Is there a way to combine both versions? That means that when calling calc(1,1) we could also call calc(1)(1) and both would still return 2.

Or calc(1,2,3) calc(1,2)(3) calc(1)(2,3) would all return 6

Upvotes: 0

Views: 278

Answers (4)

Marcio
Marcio

Reputation: 622

So here's the closest i've come to my problem. With the code below it works if i add a + sign in front of the function so +calc(1,2,3) +calc(1,2)(3) +calc(1)(2)(3) +calc(1,2)(1)(3,0,1) will all work

function calc(){
    var args = [].map.call(arguments, function(a) {
        return a;
    });
    var recursiveSum = function(arr) {
        return +arr.reduce(function(a, b) {
            return +a + (b && b.reduce ? recursiveSum(b) : +(isNaN(b) ? 0 : b));
        }, 0);
    };
    calc.__proto__.valueOf = function() {
        return recursiveSum(args);
    };
    return calc.bind(this, args);
}

Upvotes: 1

Grundy
Grundy

Reputation: 13380

Yet another way

function sum() {
  function add(a, b) {
    return a + b;
  };

  var num = [].reduce.call(arguments, add);

  function inner() {
    return sum.apply(this, [].concat.apply([num], arguments));
  }
  inner.toString = inner.valueOf = inner.toJSON = function() {
    return num;
  }
  return inner;
}

function o(prepend, val){
  document.getElementById('res').innerHTML += (prepend? (prepend+": "):"")+(typeof val == 'string'? val : JSON.stringify(val)) + '<br />';
}

o('sum(1,2,3)(4,5,6)(7)',sum(1,2,3)(4,5,6)(7));
o('sum(1,2,3)',sum(1,2,3));
o('sum(1)(2,3)', sum(1)(2,3));
o('sum(1,2)(3)',sum(1,2)(3));
o('sum(1,2)(3)+"4"',sum(1,2)(3)+"4");
o('sum(1)(2,3)+4',sum(1)(2,3)+4); //10
<div id="res"></div>

Upvotes: -1

Matt Burland
Matt Burland

Reputation: 45155

If it would be possible in some way to do something like that. calc(infinite args)(infinite args) = some result

This is maybe the closest I can imagine to what you want:

function calc(a,b) {                     // we expect a and b to be arrays
    if (Array.isArray(a)) {              // check if a is an array
        if (b === undefined) {           // if we don't have a b
            return calc.bind(null, a);   // return a curried function
        }
        if (!Array.isArray(b)) {         // if b isn't an array
            return calc(a, [].splice.call(arguments,1));    // turn all the arguments 
                                                            // after a into an array and 
                                                            // call the function again
        }
        // if both a and b are arrays, just calculate the sum and return it
        var aSum = a.reduce(function(p,c) { return p + c },0);
        return b.reduce(function(p,c) { return p + c}, aSum);
    }
    // if a was not an array, turn the arguments into an array and call again
    return calc([].splice.call(arguments,0));
}

console.log(calc(1,2)(null));      // 3
console.log(calc(1,2)(3,4));       // 10
console.log(calc(1)(3,4));         // 8
console.log(calc(1,2,3)(null));    // 6

The limitation here is that can't do calc(1,2) because it returns a function, so if you want the result, the only way to do it is to call it and pass null.

The idea here is that we have a function that will take two arrays, a and b and sum them together. If a isn't an array, we will take all the arguments that were passed and turn it into an array and then call the function again passing that array as a.

If a is and array and b is undefined, then we return a function curried with a. If a is an array and b isn't an array, then we turn all the arguments (except a) into and array and call the function again. If b is an array then we just do the calculation and return the result.

Upvotes: 0

imma
imma

Reputation: 4912

It needs to know how many arguments to do the calculation after ;-)

You can make a function that turns things into that sort of function something like this :

function curry(f){
  var args = [];
  return addargs;
  function addargs(){
    args=args.concat([].slice.call(arguments));
    return args.length<f.length? addargs.bind(this) : f.apply(this,args);
  }
}

Then you can do something like this :

var calc = curry(function(a,b,c){return a+b+c});
calc(1,2)(3); // = 6

but you can't have it take a variable number of arguments and curry those - it wouldn't know when to return the answer

Upvotes: 1

Related Questions