问题
Recently i faced one problem in hackerrank which has to calculate multiplication operation and has to return the answer. For example
function multiply(a,b) {
return a*b;
}
Now here is the problem the function might call in different ways such as
multiply(4,5);
multiply(4)(5);
multiply(4)(5)(6);
I know we have to closure apporach for the second one which is multiply(4)(5). I had written code for that
function multiply(a,b) {
return function(b) {
return a*b;
}
}
Now what if its been multiply function has been called with 3 arguments multiply(4)(5)(6). How can i identify how the function has been called and how can i write a common solution for all the inputs.
Any help is appreciated. Thanks
回答1:
To have an unlimited callchain you need some js trick:
function multiply(...args){
//make the chain endless through using some recursive like stuff:
const func = (...args2) => multiply(...args,...args2);
//the chains endpoint:
func.valueOf = function(){
return args.reduce((a,b) => a*b);
};
return func;
}
The problem of such variable currying is that its infinite, so theres ( usually ) no way to end it.
multiply(1) // func :/
However in javascript its possible to assign methods to functions so we can easily somewhen call the method instead of the function to end the chain:
multiply(1)(2)(3).valueOf()
So you can simply do:
console.log(
+multiply(1,2,3)(4)(5),
+multiply(1,2)
);
The + is equal to valueOf and is therefore neccessary to end the chain, however this call to valueOf is inferred by many operations in javascript ( all math operations like - * /).
回答2:
The Problem with this approach is the ambiguity of the returned value. It has to be a Number
AND a function
, both at the same time.
due to the valueOf()
method you can simulate that behaviour, but I'd rarely reccomend to use that in production.
const wrap = (accumulator, value) => {
let fn = (...args) => args.length? wrap(accumulator, args.reduce(accumulator, value)): fn;
fn.valueOf = () => value;
return fn;
}
let mul = wrap((a,b) => a*b, 1);
console.log(+mul(4,5));
console.log(+mul(4,5)(6));
console.log(mul(4,5) * 6);
console.log(+mul(4)(5,6)(7)(8,9)()(10));
//or maybe a different operation?
let add = wrap((a,b) => a+b, 0);
console.log(+add(1,2,3,4,5));
.as-console-wrapper{top:0;max-height:100%!important;}
Downside of this approach, it's made so flexible (and ambigious), it's pretty much useless since it's way too hard to grasp what result you'll get when using this somewhere inline. What if some functions you'll feed this into do a typecheck? Then you're in trouble.
回答3:
You're close, what you are trying to do is called currying, a technique that takes advantage of closures.
In this case, your function should only take one argument at a time:
function multiply(a) {
return function(b) {
return a * b;
}
}
Now you could say
function multiplyBy5 = multiply(5);
console.log(multiplyBy5(4)); //would log 20
If you're only expecting at max 3 arguments you could do:
function multiply(a) {
return function(b) {
return function(c) {
c = c || 1;
return a * b * c;
}
}
}
Which makes c an optional argument. You can call that function by
multiply(4)(5)()
//or
multiply(4)(5)(6)
来源:https://stackoverflow.com/questions/47286900/identify-how-the-function-has-been-called-in-closure-javascript