I am a little bit confused on the exact execution timeline in which the following function runs. I saw this code on the MDN guide:
function foo (i) {
if (i<0) return;
console.log('begin' + i);
foo(i-1);
console.log('end' + i);
}
Based on my knowledge of how functions work, this is what I thought, though my knowledge is inadequate:
I think when the function foo(3) is called, it will go to the function and check the condition if i < 0, then it will run the next code console.log('begin:' + i).
After that, the next line of code is executed, since JavaScript execute line by line, the next line of code which is foo(i-1) will be executed.
It takes the current value of i which is 3 minus 1, so foo(2) is called and the execution continues.
My question is: Is my understanding correct?. If no please explain what this code is doing, otherwise can anyone explain breiefly when will the function stop being called?
What happens when you call foo(3) is as follows (in pseudocode):
execute foo(3)
3 is greater than 0 thus log(begin 3)
execute foo(2)
2 is greater than 0 thus log(begin 2)
execute foo(1)
1 is greater than 0 thus log(begin 1)
execute foo(0)
0 is equal to 0 thus log(begin 0)
execute foo(-1)
-1 is less than 0 thus return
log(end 0)
log(end 1)
log(end 2)
log(end 3)
return
It works like this:
foo(3)->
if (3 < 0)
return
console.log('begin' + 3)
if (2 < 0)
return
console.log('begin' + 2)
if (1 < 0)
return
console.log('begin' + 1)
if (0 < 0)
return
console.log('begin' + 0)
if (-1 < 0)
return
console.log('end' + 0)
console.log('end' + 1)
console.log('end' + 2)
console.log('end' + 3)
Related
I am working my way through the javascript course on freecodecamp and im confused with the lesson on recursive functions.
I am having difficulty understanding the following code:
function sum(arr, n) {
if(n<=0) {
return 0;
} else {
return sum(arr, n-1) + arr[n-1];
}
}
sum([10,20,30,40], 3);
The specific part im struggling with is this:
arr[n-1];
would the return line not be returning sum([10,20,30,40], 3-1) + arr[3-1] resulting in 30+30 = 60?
Any help with this would be greatly appreciated. Or even pointing me in the right direction to look into this further.
Thanks
Let's write the original code in a more intuitive way by putting arr[n-1] first. This way we can keep expanding each call to sum() to the right.
But first let's note down what sum(arr, n) call will return for each n
if n > 0 => arr[n-1] + sum(arr, n-1)
if n == 0 => 0
n == 3 => arr[2] + sum(arr, 2)
n == 2 => arr[1] + sum(arr, 1)
n == 1 => arr[0] + sum(arr, 0)
n == 0 => 0
Now we expand our steps:
sum(arr, 3)
== arr[2] + sum(arr, 2) // expand sum(arr,2) where n = 2
== arr[2] + arr[1] + sum(arr, 1) // expand sum(arr,1)
== arr[2] + arr[1] + arr[0] + sum(arr,0) // expand sum(arr,0)
== arr[2] + arr[1] + arr[0] + 0
== 30 + 20 + 10 + 0
Test with
function sum(arr, n) {
console.log(`calling sum(arr, ${n})`);
if(n<=0) {
console.log(`returning 0`);
return 0;
} else {
console.log(`calculating [sum(arr, ${n-1}) + ${arr[n-1]}]`);
let s = sum(arr, n-1);;
console.log(`returning [sum(arr, ${n-1}) + ${arr[n-1]}] = [${s} + ${arr[n-1]}]`);
return s + arr[n-1];
}
}
sum([10,20,30,40], 3);
The output will be:
calling sum(arr, 3)
calculating [sum(arr, 2) + 30]
calling sum(arr, 2)
calculating [sum(arr, 1) + 20]
calling sum(arr, 1)
calculating [sum(arr, 0) + 10]
calling sum(arr, 0)
returning 0
returning [sum(arr, 0) + 10] = [0 + 10]
returning [sum(arr, 1) + 20] = [10 + 20]
returning [sum(arr, 2) + 30] = [30 + 30]
Two other classic examples of simple recursive functions are factorial and fibonacci, because those two formulas itself are recursive. Multiplication could also be computed recursively, if you think as a * b being a + (a + ...) where a is added b times.
If you're trying to code those functions, there's a hint to code this last example:
5 * 10 is equal to 5 + 5 * 9, which is equal to 5 + 5 + 5 * 8 and so on.
sum([10,20,30,40], 3-1) Will call sum function again, think about it.
Recursive functions usually operate with a value that is immediately accesible and another value that they obtain by calling themselves until a base case is reached.
In this example, that accesible parameter is one of the numbers of an array and the operation is just a simple addition. The base case will be reached when the function's parameters satisfy the if condition.
Think of the different iterations that happen here to understand how the final result is gotten:
First iteration, sum([10,20,30,40], 3)
The if condition is not fulfilled because 3 (n) is greater than 0, so the else branch's code is executed.
We have sum([10,20,30,40], 2) + arr[2]. We don't know the result of the recursive call yet but we have the value of the number located in the third position of the array, that is 30 (arrays are usually considered to start from 0).
Second iteration, sum([10,20,30,40], 2)
Again, this is not the base case yet (if branch), so we have:
sum([10,20,30,40], 2-1) + arr[2-1] ->
sum([10,20,30,40], 1) + arr[1] ->
sum([10,20,30,40], 1) + 20
Third iteration, sum([10,20,30,40], 1)
At this point, we have 30 and 20 as "partial results". These numbers and the results of the remaining iterations will all be added up, because that's what the code in the else branch does, an addition.
sum([10,20,30,40], 1-1) + arr[1-1] ->
sum([10,20,30,40], 0) + arr[0] ->
sum([10,20,30,40], 0) + 10
Another partial result has been added: 10.
Forth and final iteration, sum([10,20,30,40], 0)
The base case is finally reached, the if branch's code is executed and the recursion stops right here because there isn't another call to the recursive function in this code. The result of sum([10,20,30,40], 0) is 0 because the code returns 0.
Now that we have reached the end of the recursion, we can retrace our steps.
• The result of the third iteration is sum([10,20,30,40], 0) + 10. We know this is 0 + 10 now, so 10.
• The result of the second iteration is sum([10,20,30,40], 1) + 20 = 10 + 20 = 30.
• And the result of the first call and the original call to the function is sum([10,20,30,40], 2) + 30 = 30 + 30 = 60.
Recursive functions are really tricky at first but once you understand this logic of partial results that are "accumulated" until the moment the base case is reached, they get easier. Just take your time.
So,
I'm starting to learn how to code and now I have only one problem.
My code is functioning very well, until he says that the numbers 1 and 0 are prime.
I already tried to add them to exception, change the calculation and still printing that is prime.
Here's the code:
function testPrimeNumber(num)
{
Number(num)
for (var i = 2; i < num; i++)
{
if (num % i == 0)
{
return ("The number "+num+" isn't prime.");
}
else {}
}
return ("The number "+num+" is prime.");
}
console.log("result for 1:", testPrimeNumber(1));
What am I doing wrong here?
You should test for 0 and 1 at the start of the function. At the moment your code is skipping the for loop (because 1 < 2 and 0 < 2) and going straight to saying the number is prime.
code:
function OneDecremented(num) {
num = num.toString()
var count = 0
for(i = 1; i < num.length; i++) {
if(num[i - 1] - num[i] === 1){
count++
}
}
return count
}
console.log(OneDecremented(9876541110))
so I'm struggling to understand two things:
what's the difference between i and num[i]
I don't understand how the calculation is happening inside the if statement, could someone break it down?
sorry if these questions sound too silly, I'm new to JS and couldn't really get my head around the arithmetic calculations. Thank you for you time.
That code is poorly written for few reasons, but most importantly, it leaks the i reference globally so, let's start with a better version:
function OneDecremented(num) {
var str = num.toString();
var count = 0;
for(var i = 1; i < str.length; i++) {
if(str[i - 1] - str[i] === 1)
count++;
}
return count;
}
Strings, in modern JS, can be accessed like arrays, and the index returns the char at the index position:
if(str[i - 1] - str[i] === 1)
// is the same as
if ((str.charAt(i - 1) - str.charAt(i)) === 1)
Once retrieved each char, the code does an implicit "char to number" conversion, thanks to the - operator, but if it was a + instead, it would've concatenated the two chars as string instead (so, be careful).
It's always better to be explicit, but if you know how - works, it does the job for this task.
The loop starts from 1, and it checks that the char at i - 1, which is in the first iteration the char at index 0, minus the current char, is 1, meaning the current char is one less the previous.
When that's the case, the counter sums up.
Andrea and Mitya already nailed it.
The next step could be switching to a first class based approach like using a specific Array method such as reduce.
Such an approach, if implemented correctly, usually improves readability/maintainability of code and allows for better code-reuse.
For the example provided by the OP one could write two functions, the actual method, which gets the count and the above mentioned first class reducer functionality. Since reduce is a standard way of how arrays can be processed the arguments-precedence of the reducer/callback is well specified too ...
[/* ... */].reduce(function(accumulator, currentValue, currentIndex, currentlyProcessedArray) {
// implement reducer/aggregation/accumulator logic here.
// the return value serves as the
// new `accumulator` value within
// the next iteration step.
// thus, always return something! ... e.g ...
return (accumulator + currentValue);
});
function aggregatePrecursorAndDecrementedSuccessorCount(count, char, idx, arr) {
const precursorValue = Number(arr[idx - 1]);
const incrementedCurrentValue = (Number(char) + 1);
const isValidCount = (precursorValue === incrementedCurrentValue);
return (count + (isValidCount ? 1 : 0));
//return (count + Number(isValidCount)); // cast boolean value to either 1 or 0.
}
function getPrecursorAndDecrementedSuccessorCount(int) {
return String(int) // - assure/typecast always a/into string value.
.split('') // - split string value into an array of single characters.
.reduce(aggregatePrecursorAndDecrementedSuccessorCount, 0);
}
console.log(getPrecursorAndDecrementedSuccessorCount(9876541110));
.as-console-wrapper { min-height: 100%!important; top: 0; }
what's the difference between i and num[i]
i is the iteration key, i.e. 0, 1, 2 etc, for as many characters are in the stringified number. num[i] is the character at the index i in the string, i.e. num[i] where i is 0 == 9 (the character in the string at index 0).
I don't understand how the calculation is happening inside the if statement, could someone break it down?
That says: If the calculation of the number at index i-1 of the string, minus the current number being considered (at index i in the string) minus is 1, then increment count.
Step by step for the actual number used:
9 - has no previous character; calculation (undefined - 9) does not equate to 1
8 - previous char is 9; (9 - 8) == 1; increment count
7 - ditto
6 - ditto
5 - ditto
4 - ditto
1 - previous char is 4; calculation (4 - 1) does not equate to 1
1 - previous char is 1; calculation (1 - 1) does not equate to 1
1 - ditto
0 - previous char is 1; (1 - 0) == 1; increment count
I want to visually understand what happens when this recursive code is run. I don't understand how the end result is 9.
In my mind, the f(x - 1) will iterate until it returns 5, then you add 1 which equals 6.
let f = x => {
if (x === 0) {
return 5
}
return 1 + f(x - 1)
}
let y = f(4)
console.log(y)
Thank you for your time
You have the reasoning backwards. It isn't that one gets added once at the very end, one gets added after each resursive call returns. Think about this line:
return 1 + f(x - 1)
Once the recursive f call returns, one is added to that result. Then this recursive call returns, and one is added again. That keeps happening until the initial call returns.
Since one is added once per recursive call, and it will recurse four times, and the base case returns 5, this function ends up basically just calculating
1 + 1 + 1 + 1 + 5 == 9
You could take a level for an indention of a log and look which level has an input and return value.
function f(x, level = 0) {
console.log(level, '>>'.repeat(level + 1), x);
var v = x === 0
? 5
: 1 + f(x - 1, level + 1);
console.log(level, '<<'.repeat(level + 1), v);
return v;
}
console.log('result', f(4));
.as-console-wrapper { max-height: 100% !important; top: 0; }
I don't understand this!
function rec(arg){
console.log(arg);
if(arg == 3)
return arg;
else
rec(arg + 1);
}
var i = rec(0);
console.log(i);
//0
//1
//2
//3
//undefined
Why inside the function 'arg' has a value but when it`s time to return it('arg == 3') it gives me 'undefined'?
Here is another one
function power(base, exponent) {
console.log(exponent);
if (exponent == 0)
return 1;
else
return base * power(base, exponent - 1);
}
console.log(power(2, 3));
//3
//2
//1
//0
//8
Why does it return '8' when 'exponent' is '0' inside the function and it should return '1'!
I now understand that I don`t understand how JS works.
In your first example, you should write the line
if(arg == 3)return arg;else rec(arg + 1);
as this
if(arg == 3)return arg;else return rec(arg + 1);
In the second example, you should change the line
return base * power(base, exponent - 1);
to
return Math.pow(base, exponent);
(actually, you should just replace the whole function with a call to pow(), you don't need to re-write it)
EDIT:
In the first example, you should have broken it town into multiple lines, then you would have seen it as a simple mistake. So, like this:
if(arg == 3)
return arg;
else
return rec(arg + 1);
And for posterity and good habits, it should be like this (in other words, use brackets)
if(arg == 3) {
return arg;
}
else {
return rec(arg + 1);
}
In your second example, you used recursion when you shouldn't have. A simple call to Math.pow() was all that you needed. Unless you wanted to log the behavior, no need to re-write the function.
EDIT:
My mistake after reading your comment. I apologize.
The flow goes like this:
console.log(power(2, 3));
then to the line
if (3 == 0)
return 1;
else
return 2 * power(2, 3 - 1); //power() returns 4, we return 8
//same as: return 2 * power(2, 2);
then to the line
if (2 == 0)
return 1;
else
return 2 * power(2, 2 - 1); //power() returns 2, we return 4
//same as: return 2 * power(2, 1);
then to the line
if (1 == 0)
return 1;
else
return 2 * power(2, 1 - 1); //power() returns 1, we return 2
//same as: return 2 * power(2, 0);
then to the line
if (0 == 0)
return 1;
else
// moot
Because your not telling it to.
function rec(arg){
console.log(arg);
if(arg == 3)return arg;
else rec(arg + 1); // < --- not a return statement
}
var i = rec(0);console.log(i);
The else block is not a return statement.
And as for your second question, that is because the 0 is coming from a nested call to the function somewhere down the line:
base * power(base, exponent - 1); // power(base, exponent - 1) would return 1 here, and base is probably 8 at that moment, so 8 * 1 would return you 8
While the previous answers give factually correct information, they don't address what you're misunderstanding.
In both cases, you're expecting the return form the recursive function to be the return of the last (innermost) invocation. That's incorrect.
The return from the innermost invocation is given to the second-to-innermost invocation; and then this continues until finally the outermost invocation returns. Only the outermost invocation's return value is seen by the caller.
So in the first example, you call rec(0).
Then rec(0) calls rec(1),
which calls rec(2),
which calls rec(3).
Then rec(3) returns the value 3 to rec(2) (because that's still running).
Then rec(2) exits without a return to rec(1)
which exits without a return to rec(0)
which exits without a return to the caller.
So the caller sees the return as undefined.
In the second case, yes, the last invocation of power() is power(2,0) which returns 1... to the running invocation of power(2,1)
which returns 2 to the running invocation of power(2,2)
... and so on until the final return seen by the caller is 8.
By the way, this is not "recursion in JavaScript". This is recursion.