8

This was an interview question which I haven't yet been able to figure out. Consider the following:

function recurse(a) {
    return function(b) {
        console.log(a + b);
    }
}

//This will log '5' in the console
recurse(2)(3);

Now I was asked to write a function that will take n number of arguments and work the same way by logging the final summation of the argument values. Meaning:

//This should log '13'
recurse(2)(3)(1)(7)

How can such a function be written? I have tried thinking over it in terms of recursion, dynamic arguments etc. But haven't been able to write down anything concrete.

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
Rutwick Gangurde
  • 4,772
  • 11
  • 53
  • 87
  • 2
    Um you do not call recursive functions this way, it needs to call itself to be recursive. When you do `recurse(2)` you effectively return `function(b){console.log(a+b)}` that is aware of the `a = 2` which you passed to it's closure. You are not getting the result via recursion. – Dellirium Jul 28 '16 at 12:00
  • 1
    This is currying of variadic functions and a bad idea. You should write that as an answer. –  Jul 28 '16 at 12:02
  • @Dellirium And yet, the general solution (for `n` curried arguments) will be recursive! – A. Vidor Jul 28 '16 at 12:02
  • @this-vidor idk whay do you refer to here, the above code throws an error when called with more then 2 arguments – Dellirium Jul 28 '16 at 12:05
  • 1
    @Dellirium You haven't read the question, which asks exactly how one would create such a function for an arbitrary number of arguments. – A. Vidor Jul 28 '16 at 12:05
  • @Dellirium I am not sure of the solution or how to even describe my problem. My puny brain seems to think of recursion since I am multifolding what the single level function returns. – Rutwick Gangurde Jul 28 '16 at 12:34
  • Currying. Interesting, I didn't know this term exists. – Rutwick Gangurde Jul 28 '16 at 12:56

3 Answers3

6

You could return the function and implement toString and valueOf methods.

function recurse(x) {
    var sum = x;  

    function f(y) { 
        sum += y;
        return f;
    }; 
    f.toString = function () { return sum; }; // for expecting string, 1st log
    f.valueOf = function () { return sum; };  // for expecting number, 2nd log

    return f;
}

console.log(recurse(2)(3)(4));
console.log(recurse(2)(3) + recurse(4)(5));
console.log(recurse(2)(40) == 42);  // converts to expected type Number
console.log(recurse(2)(40) === 42); // checks type Function
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • Why people always propagate this hack? –  Jul 28 '16 at 12:04
  • 1
    @LUH3417 What hack? – A. Vidor Jul 28 '16 at 12:05
  • Well isn't changing the `.toString` and `.valueOf` considered a hack? – Dellirium Jul 28 '16 at 12:06
  • @Dellirium Not by me. It might be **useful** for the community if you included links to a relevant discussion of the subject. – A. Vidor Jul 28 '16 at 12:10
  • 2
    @Dellirium: No, it's normal for objects to define their own methods, including those ones. But *this use* of that could arguably be called a hack, since `f = recurse(2)(3)` is **not** going to put `5` in `f`. The only reason we see `9` in the in-snippet console is that it calls `toString`; a real console shows a function (followed by its toString result). – T.J. Crowder Jul 28 '16 at 12:10
  • @T.J.Crowder fair point. – Dellirium Jul 28 '16 at 12:14
  • 2
    this is solving the asked problem, but the actual problem is the requirement itself. As Nina showed you can modify the function that it looks (and kind of behaves) like a number, but it is none, and this might lead to problems, when using this thing. Because although `console.log(recurse(2)(3), recurse(2)(3)==5)` will show `5, true`, this will fail: `recurse(2)(3)===5`. and unless you also add a `toJSON`-function, this "number" will disappear on serialization. **conclusion:** best solution you can get for a problematic requirement, and most likely buggy (in it's usage, not implementation) – Thomas Jul 28 '16 at 12:36
  • @Thomas Technically, this doesn't solve the assigned problem, since the function is supposed to do the logging itself! Since it was an interview question, I assume the spirit of the question is the general mechanism for currying an arbitrary number of arguments. – A. Vidor Jul 28 '16 at 12:39
  • It works, but I can see `function` along with the answer. Meaning `recurse(4)(4)(4)` prints `function 12` in the console. – Rutwick Gangurde Jul 28 '16 at 12:39
  • @Thomas Your comment is actually the best answer! –  Jul 28 '16 at 12:43
  • Up-voting this answer. It's pretty close. – Rutwick Gangurde Jul 28 '16 at 12:55
6

Here's the simplest version I could think of:

function add (a) {
  return function (b) {
    return b == null ? a : add(a+b);
  }
}

console.log( 
  add(2)(3)() 
);
console.log(
  add(10)(100)(1000)(4)() 
);

In es6, it's compact!

let add = a => b => b == null ? a : add(a+b);

Note

The trouble with your question is that a function can either return a Function or a Number. The following code:

let result = add(2)(3);

is equivalent to:

/*1*/ let partialResult = add(2);
/*2*/ let result = partialResult(3);

In line 1, add doesn't know whether it is being called with the last argument or not! In other words, it doesn't know whether partialResult should be a number, or a function that will be called with another argument.

Nina Scholz gives you a solution where partialResult will behave like a number when treated like a primitive, and like a function when called like a function. In my solution, partialResult always acts like a function, and returns a sum when called without an argument.

The first approach requires a language-specific mechanism for "behaving like a number" under certain conditions. If your interview was about JavaScript, it's the best approximation! But in a language-agnostic context, what you are asking for is impossible.

A. Vidor
  • 2,502
  • 1
  • 16
  • 24
2

It is not requested that the recursion actually returns anything. It should just log.

And because the requirement is to log the final summation of the argument values, we can assume that it's perfectly fine to log the intermediate summations as well (unless it was explicitly forbidden in the full problem statement).

So, I suppose the interviewer would have accepted the following solution. It may actually be what he/she was expecting, given the initial source code.

function recurse(a) {
  console.log(a);
  return function(b) {
    return recurse(a + b);
  }
}

recurse(2)(3)(5)(7)

If you really want to display only the last summation, you can either:

  1. Cheat a little by using a console.clear() (not my preferred solution):

function recurse(a) {
  console.clear();
  console.log(a);
  return function(b) {
    return recurse(a + b);
  }
}

recurse(2)(3)(5)(7)
  1. Use a combination of clearTimeout() and setTimeout(). Here, we take advantage of the single-threaded nature of Javascript which prevents the callback from being executed until the expression is fully evaluated.

var t;

function recurse(a) {
  t && clearTimeout(t);
  t = setTimeout('console.log(' + a + ');', 0);
  return function(b) {
    return recurse(a + b);
  }
}

recurse(2)(3)(5)(7)
Arnauld
  • 5,847
  • 2
  • 15
  • 32