0

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

asked Aug 5, 2015 at 14:02
8
  • Use varargs, then it is possible. Commented Aug 5, 2015 at 14:04
  • Somebody posted this link in another question, about currying functions. It's worth a read. Commented Aug 5, 2015 at 14:06
  • possible duplicate of Javascript sum function Commented Aug 5, 2015 at 14:14
  • 1
    You seem to be asking for a function that can return two different things without giving it any indication of what you want. So calc(1,2) should return 3 or maybe a function that adds 3 to whatever other parameter you pass it (so calc(1,2)(3) would work). That's impossible. You are asking for a function that can read minds. If you restrict it to a set number of parameters, then it's just currying which is certainly a duplicate. Commented Aug 5, 2015 at 14:21
  • 1
    If you have a function calc(a,b) and you want to be able to do calc(1,2) and calc(1)(2), then that's just currying and my linked question has several answers. If you function is calc() and you want to be able to do calc(1,2), calc(1)(2), calc(1,2)(3), calc(1,2)(3,4), then that is impossible. How would your function know when you call calc(1,2) to return a function or the sum of the values? Commented Aug 5, 2015 at 14:26

4 Answers 4

1

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

answered Aug 5, 2015 at 14:26
Sign up to request clarification or add additional context in comments.

1 Comment

ie you can't have calc(1,2) give 3 and calc(1,2)(3) give 6 because calc(1,2)(3) would be doing 6(3) and 6 isn't a function
1

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);
}
answered Aug 6, 2015 at 11:25

7 Comments

Overriding the prototype.valueOf is a little dicey, and the leading with a + is a bit hacky, but otherwise very clever.
in your case when call like +calc(1,2,3) then this refers to window
@Grundy if you have some improvement let me know. It's just for fun code i'm not gona use this in production obviously.
i think instead map better use slice for getting array from arguments, and also instead this use some another nested function or object
@Grundy Why do you think slice would be better than map? Any advantage one over the other in some way? Not sure what you mean with this replacing with object or nested function.
|
0

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.

answered Aug 5, 2015 at 14:49

Comments

-1

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>

answered Aug 5, 2015 at 15:27

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.