0

Here's the code

function rangeOfNumbers(startNum, endNum) {
  return startNum === endNum
    ? [startNum]
    : rangeOfNumbers(startNum, endNum - 1).concat(endNum);
}

I understand that until startNum equals endNum it will recall itself, but the thing that I don't understand is where the value is stored?

Say for example it's rangeOfNumbers(3,6) So it's gonna be like this:

6-1
5-1
4-1

Right? And each time the numbers are added to the array and we get [3,4,5,6], but I don't understand how and where it stores this array.

If I'm not mistaken, concat merges two or more arrays, but there are no arrays.

I just want to have a full understanding of it. Otherwise, I won't remember it and won't be able to use it.

KnowNothing
  • 37
  • 10

3 Answers3

3

As soon as the breaking condition is meet (startNum === endNum), an array is returned ([startNum]). Such object has a concat function which bubbles up another array and so on until the first call.

In resume: The array starts at the breaking condition and endNum is concatenated on each return value, which again, is an array.

E. Mancebo
  • 629
  • 3
  • 10
1

When a block (something that starts with { and contains statements) is entered into, a new "variable environment" is created. You could think of this as something that maps each identifier for that block execution to its value.

Each time a function is called, a new such environment is created.

In this case, the parameters startNum and endNum are stored in an environment the first time the function is called. Then, when the interpreter runs across

rangeOfNumbers(startNum, endNum - 1).concat(endNum);

The currently running function (the one linked to the environment just described) gets suspended, and a new function is put onto the call stack, creating another environment. The process repeats itself until the end of the recursive logic is reached and [startNum] is returned (or the stack is blown). At that point, you have a bunch of rangeOfNumbers functions in progress, each with their own environments. At that point, you could imagine it as something like

rangeOfNumbers { startNum: 3, endNum: 5 } (this is the intial call; currently suspended)
  rangeOfNumbers { startNum: 4, endNum: 5 } (currently suspended)
    rangeOfNumbers { startNum: 5, endNum: 5 } (executing, about to return)

The innermost function returns its [startNum] and terminates, and so the last function resumes, now with the useable return value:

: rangeOfNumbers(startNum, endNum - 1).concat(endNum);

gets evaluated to, when the endNum is 5:

: [5].concat(endNum);

The process continues up the stack until all of the recursive calls are finished, and you just have the initial

rangeOfNumbers { startNum: 3, endNum: 5 }

which then finishes itself.

So, while the recursive calls are going on, the values from the prior calls are stored in each of those calls' environments.

If I'm not mistaken, concat merges two or more arrays, but there are no arrays.

[startNum] is an array returned at the innermost point of recursion. concat can also create a new array by taking one array as an argument, and the value to append as another. For example, [5].concat(4) evaluates to [5, 4].

CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
1

If I'm not mistaken, concat merges two or more arrays, but there are no arrays.

You are absolutely right about this. endNum is not an array. However, if you read further down in the docs, the argument supplied to concat can either be an array or value(s).

Parameters

valueN Optional

Arrays and/or values to concatenate into a new array. If all valueN parameters are omitted, concat returns a shallow copy of the existing array on which it is called. See the description below for more details.

Javascript functions are variadic by design, so it looks like the concat method takes advantage of this to accept individual arguments to append to a copy of the existing array.


I understand that until startNum equals endNum it will recall itself, but the thing that I don't understand is where the value is stored?

It may help if you write the function using explicit variable names

function rangeOfNumbers(startNum, endNum) {
  if (startNum === endNum) {
    return [startNum];
  }
  const currentRange = rangeOfNumbers(startNum, endNum - 1);
  return currentRange.concat(endNum);
}

As you can see, the value (or endNum) is stored inside the array returned by calling the function for the range [startNum, endNum - 1].

The trickiest part about recursion lies in the fact that you don't see where those intermediate values are stored, until as if by magic, we get to the base case and lo and behold, we have an array!

The answer is that the array returned by rangeOfNumbers(startNum, endNum - 1) is kept in stack memory until it needs to be returned. The discussion of stack vs heap will be quite off-topic here, but it is pretty much covered here:

What and where are the stack and heap?

smac89
  • 39,374
  • 15
  • 132
  • 179
  • 1
    Thank you, i think with your explanation i understand it way better now. I really appreciate your help and your time that you took for me. Have a very good one! – KnowNothing Jan 17 '22 at 11:59