Currying function with unknown arguments in JavaSc

2020-06-29 07:56发布

问题:

In a recent interview, I was asked to write a function that adds numbers and accepts parameters like this:

add(1)(2)(3) // result is 6
add(1,2)(3,4)(5) // result is 15

The number of parameters is not fixed, and the arguments can be either passed in sets or individually.

How can I implement this add function?

回答1:

Given your examples, the number of parameters is fixed in some ways.

As @ASDFGerte pointed out, your examples seem to return the result after three invocations. In this case a simple implementation without introducing terms like variadic and currying could be

function add(...args1){
  return function(...args2){
    return function(...args3){
      return args1.concat(args2).concat(args3).reduce((a,b)=>a+b)}}}
                
console.log(add(1)(2)(3))
console.log(add(1,2)(3,4)(5))

Every invocation accepts a variable number of parameters.

However it would be nice to generalize the construction of this nested functions structure and you can accomplish that with currying.

But if you want to allow an arbitrary number of invocations, when you should stop returning a new function and return the result? There is no way to know, and this is a simple, unaccurate and partial explanation to give you the idea of why they said you cannot accomplish what they asked you.

So the ultimate question is: is it possible that you misunderstood the question? Or maybe it was just a trick to test you

Edit

Another option would be to actually invoke the function when no arguments are passed in, change the call to add(1)(2)(3)()

Here an example recursive implementation

function sum (...args) {
  let s = args.reduce((a,b)=>a+b)

   return function (...x) {
     return x.length == 0 ? s : sum(s, ...x)
    };
}
console.log(sum(1,2)(2,3,4)(2)())

At every invocation computes the sum of current parameters and then return a new function that:

  • if is invoked without parameters just return the current sum
  • if other numbers are passed in, invokes recursively sum passing the actual sum and the new numbers


回答2:

I'm a bit late to the party, but something like this would work (a bit hacky though in my opinion):

const add = (a, ...restA) => {
  const fn = (b, ...restB) => {
    return add([a, ...restA].reduce((x, y) => x + y) + [b, ...restB].reduce((x, y) => x + y))
  };
  fn.valueOf = () => {
    return [a, ...restA].reduce((x, y) => x + y)
  };
  return fn;
}

This function returns a function with a value of the sum. The tests below are outputing the coerced values instead of the actual functions.

console.log(+add(1,2)(3,4)(5)); // 15
console.log(+add(1)) // 1
console.log(+add(1)(2)) // 3
console.log(+add(1)(2)(3)) // 6
console.log(+add(1)(2)(3)(4)) // 10

Since it's a currying function, it will always return another function so you can do something like this:

const addTwo = add(2);
console.log(+addTwo(5)); // 7