Some recursive principles to keep in mind: scope, nesting, and stepping in and out.
console.log(
calcDays(
[2, 2, 3, 4, 2],
0
)
);
function calcDays(parcels, days) {
console.log('variable days =', days);
if (parcels.length === 0) { return days; }
days++;
let min = parcels[0];
parcels.forEach((num) => {
if (num < min) min = num;
})
for (let i = 0; i < parcels.length; i++) {
parcels[i] = parcels[i] - min;
if (parcels[i] <= 0) {
parcels.splice(i, 1);
i--;
}
}
calcDays(parcels, days)
}
Scope: (execution context)
Having the parameters provided, calcDays() executes 4 times. console.log('variable days =', days);, at the top of the function block, writes to the console:
> variable days = 0
> variable days = 1
> variable days = 2
> variable days = 3
It appears all 4 of the days variables are the same variable. They're not. There are 4 different days variables here, each within it's own scope.
With each recursion, a recursive function creates its own scope. And that scope is nested inside the execution of the recursive function that called the current recursion. It operates as you had 4 self-executing functions nested one within the other:
(function (param) {
// scope 1
(function (param) {
// scope 2
(function (param) {
// scope 3
(function (param) {
// scope 4
}(param));
}(param));
}(param));
}(param));
param is a different variable within each scope. Scope 1 param is passed to the the second execution by way of function arguments and its value is assigned to Scope 2 param. The process continues all the way to the bottom of the "nest". That explains why it appears param—or days—is the same variable, but is not.
Stepping back up: (return)
As shown above, variable values are passed down the "nest" using function parameters. The caller (parent) execution of the recursive function provides its variable values to the callee (nested/child) execution by passing the parent variable values to the child execution through the function arguments. return is not necessary to carry variable values down into the "nest".
But, every step down into a "nest" must be stepped back up for each and every execution of the recursive function to finally complete. Every step out of the "nest" leaves behind the scope of the nest it is stepping out of.
When a nested execution context is stepped out of, its scope becomes inaccessible. Changes made to variables in Scope 3 are not available to Scope 2, and variables in Scope 2 are not available to Scope 1.
One way to "carry" variable values back out of the "nest", and make the values available in the parent function scope, is to return those variables from the child function scope.
As stated earlier, the recursive function example executes 4 times, however the statement return days; in the conditional block if (parcels.length === 0) { return days; } only executes once. And it only executes in the innermost nested execution, the fourth scope in this case. Therefore ONLY the third scope has access to the Scope 4 days variable value. Scope 3 completes execution, but has not returned any variable values to Scope 2, so the variable value return'ed from Scope 4 is no longer available. Scope 2 returns nothing to Scope 1, and Scope 1 returns nothing to the initial execution of the recursive function. Javascript returns undefined when a function does not return a value.
console.log(
calcDays(
[2, 2, 3, 4, 2],
0
)
);
function calcDays(parcels, days) {
console.log('variable days =', days);
if (parcels.length === 0) { return days; }
days++;
let min = parcels[0];
parcels.forEach((num) => {
if (num < min) min = num;
})
for (let i = 0; i < parcels.length; i++) {
parcels[i] = parcels[i] - min;
if (parcels[i] <= 0) {
parcels.splice(i, 1);
i--;
}
}
calcDays(parcels, days)
}
That's why it appears the "base case", if (parcels.length === 0) return result, is returning undefined to the initial execution, return calcDays(parcels, days);, occurring in the getMinimumDays() function block.
In fact, the "base case" is returning the desired value, but it's not carried all the way back out the nested recursions.
Not recommended in this case, but to illustrate the principles, using a global variable could also provide the final calculation.
let global_days;
calcDays([2, 2, 3, 4, 2], 0);
console.log(global_days);
function calcDays(parcels, days) {
while ( 0 < parcels.length ) {
days++;
let min = parcels[0];
parcels.forEach((num) => {
if (num < min) min = num;
})
for (let i = 0; i < parcels.length; i++) {
parcels[i] = parcels[i] - min;
if (parcels[i] <= 0) {
parcels.splice(i, 1);
i--;
}
}
global_days = days;
calcDays(parcels, days);
}
}