Reputation: 622
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
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
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
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
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