-1

I'm trying to learn recursion, and I found this sample function on the internet. I'm having a hard time following exactly what's happening here. The part that's most confusing to me is, I put a couple of alert commands in the final block (where the arguments are reduced to 2), to see what the values of "first" and "second" are when it gets to this point. The values come up as "4" and "3", and I add these two values right before the return. It comes up as 7. Then the same exact equation is what's actually returned, and it gives the answer "10". Can anyone explain to me how this is working and how it finally arrives at 10?

function sum() {
    var args = Array.from(arguments);
    var first = args[0];
    var second = args[1];
    if(args.length === 2) {
        alert("first:" + first + " second:" + second );
        alert("Sum: " + (first+second) ) //this alerts 7
        return first + second;           //returned value is 10
    }
    return first + sum.apply(null, args.slice(1));
}

var results = sum(1,2,3,4);
console.log(results);

2 Answers2

4

The first time through sum, it grabs 1 and 2, but args.length == 4, so it returns 1 + something still to be decided.

To find the something still to be decided, the function again calls sum (recursion), but slices off the first element, so we are left with 2,3,4 This time, it grabs 2 and 3, but args.length == 3, so it says that something still to be decided is 2 + something else.

To find something else, the function again calls sum (recursion) but again slices off the first element. Last time it had 2,3,4, so this time it has 3,4.

This time, args.lenght == 2, so it goes in, and adds up 3+4 == 7. Then your alert gets called for the first time because this is the first time it's gone through this section of the code. Finally it returns 7. This is the three layers deep.

Whew! Now we know that something else = 7, which means that something still to be decided = 2 + something else = 2 + 7 = 9, so we'll return that. This is two layers deep.

And finally, we return 1 + *something still to be decided = 1 + 9 = 10 from the first layer of sum().

The reason it alerts 7, then returns 10, is because your alert only gets called when the args.length == 2.

bwall
  • 984
  • 8
  • 22
  • Wow, it makes sense!! I was picturing the flow going through the same function over and over again, and then returning to the variable at the point of the if statement (i knew that was wrong, but couldn't think of anything else), but reading your explanation i see that the first time the return is called it kind of sets up shop there and doesn't return until it's done calling copies of the same function. Thank you!! – Jonathan Foster Oct 13 '16 at 02:40
  • And thank you for the clarification on the alert, that makes sense too now. I really appreciate you taking the time to give such a detailed answer! – Jonathan Foster Oct 13 '16 at 02:47
4

It doesn't help that the particular function your reading is a piece of trash. It's not your fault that you can't understand it effectively.

First off, that function you found is not a total function – meaning, it does not produce a sensible result for all inputs of its domain.

sum();
// RangeError: Maximum call stack size exceeded

sum(1);
// RangeError: Maximum call stack size exceeded

This is ridiculous. We might expect sum() to return 0 (perhaps NaN), but sum(1) should definitely return 1.

Second, this function is using Array.from which puts it in the ES6+ era of code...

function sum() {
   // are you f*$@ing joking me??
   var args = Array.from(arguments);

... using Array.from but still using the old arguments object? wow.

Either way... *wretch*, *gag* – this is a bad function *puke* you shouldn't be paying attention to it.


Consider the following sum function implementations. I promise you will be able to understand these with trivial effort.

function sum(numbers) {
  if (numbers.length === 0)
    return 0;
  else
    return numbers[0] + sum(numbers.slice(1));
}

console.log(sum([]));                  //= 0
console.log(sum([1]));                 //= 1
console.log(sum([1,2,3,4,5,6,7,8,9])); //= 45

It works only slightly different, accepting an array of numbers instead of variadic arguments.

// sum must be called like this now
sum([1,2,3,4,5,6,7,8,9]);

// instead of:
sum(1,2,3,4,5,6,7,8,9);

And OK, maybe supporting the variadic interface is a requirement. There's still a much better way to do it – especially if you can use ES6

// ES6 using spread syntax
function sum(n,...ns) {
  if (n === undefined)
    return 0;
  else
    return n + sum(...ns);
}

console.log(sum());                  //= 0
console.log(sum(1));                 //= 1
console.log(sum(1,2,3,4,5,6,7,8,9)); //= 45

And OK, maybe you can't use ES6. There's still a much better way to do it using pre-ES6

// ES5; relying upon old arguments object
function sum(/*numbers*/) {
  if (arguments[0] === undefined)
    return 0;
  else
    return arguments[0] +
      sum.apply(null, Array.prototype.slice.call(arguments, 1));
}

console.log(sum());                  //= 0
console.log(sum(1));                 //= 1
console.log(sum(1,2,3,4,5,6,7,8,9)); //= 45
Mulan
  • 129,518
  • 31
  • 228
  • 259
  • I'm very impressed with your ability to insert runnable code snippets into your answer. How did you do that? – bwall Oct 16 '16 at 21:10
  • When you're editing your answer, there is a *"JavaScript/HTML/CSS snippet"* button in the toolbar ^_^ kinda looks like this `[<>]` – if you're having trouble finding it, you can use `Ctrl-M` keyboard shortcut instead. – Mulan Oct 16 '16 at 22:03