how restricted is recursion in javascript? - javascript

I guess its to stop browsers getting nailed all the time by duff code but this:
function print(item) {
document.getElementById('output').innerHTML =
document.getElementById('output').innerHTML
+ item + '<br />';
}
function recur(myInt) {
print(myInt);
if (int < 10) {
for (i = 0; i <= 1; i++) {
recur(myInt+1);
}
}
}
produces:
0
1
2
3
4
5
6
7
8
9
10
10
and not the big old mess I get when I do:
function recur(myInt) {
print(myInt);
if (int < 10) {
for (i = 0; i <= 1; i++) {
var x = myInt + 1;
setTimeout("recur("+x+")");
}
}
}
Am I missing something or is this how you do recursion in JS? I am interested in navigating trees using recursion where you need to call the method for each of the children.

You are using a global variable as loop counter, that's why it only loops completely for the innermost call. When you return from that call, the counter is already beyond the loop end for all the other loops.
If you make a local variable:
function recur(int) {
print(int);
if (int < 10) {
for (var i = 0; i <= 1; i++) {
recur(int + 1);
}
}
}
The output is the same number of items as when using a timeout. When you use the timeout, the global variable doesn't cause the same problem, because the recursive calls are queued up and executed later, when you have exited out of the loop.

I know what your doing wrong. Recursion in functions maintains a certain scope, so your iterator (i) is actually increasing in each scope every time the loop runs once.
function recur(int) {
print(int);
if (int < 10) {
for (var i = 0; i <= 1; i++) {
recur(int+1);
}
}
}
Note it is now 'var i = 0' this will stop your iterators from over-writing eachother. When you were setting a timeout, it was allowing the first loop to finish running before it ran the rest, it would also be running off the window object, which may remove the closure of the last iterator.

Recursion is very little restricted in JavaScript. Unless your trees are very deep, it should be fine. Most trees, even with millions of elements, are fairly wide, so you get at most log(n) recursive calls on the stack, which isn't noramally a problem. setTimeout is certainly not needed. As in your first example, you're right that sometimes you need a guard clause to guarantee that the recursion bottoms out.

Related

Code is getting stuck somewhere in a succession of for-loops and I'm not sure why

EDIT - I changed the code to correctly declare variables below but nothing seems to have changed
I've written code using a for-loop that has to satisfy a number of criteria before executing what's within it. The problem is that, somewhere along the way, the code is getting stuck inside one of the loops, causing the computer to crash.
I've tried breaking the loop but this doesn't seem to help.
function compareKeypoints(varifiedKeypoints) {
outer_loop: for (i = 0; i < varifiedKeypoints.length; i++) {
let initialKeypoint = varifiedKeypoints[i];
for (j = 0; j < varifiedKeypoints.length; j++) {
let comparisonKeypoint = varifiedKeypoints[j];
if (initialKeypoint.part != comparisonKeypoint.part) {
if (Math.abs(comparisonKeypoint.position.x - initialKeypoint.position.x) <= 20
&& Math.abs(comparisonKeypoint.position.y - initialKeypoint.position.y) <= 20) {
if (keypointsCompatible(initialKeypoint.part, comparisonKeypoint.part)) {
console.log("Activating part: " + initialKeypoint.part);
console.log("Activated part: " + comparisonKeypoint.part);
let keypointPair = {
point_1: initialKeypoint.part,
point_2: comparisonKeypoint.part
}
console.log("Pushing parts!");
activeParts.push(keypointPair);
console.log("breaking loop!");
break outer_loop;
console.log("Loop NOT broken!!");
}
}
}
}
}
if (activeParts.length > 0) {
console.log(activeParts);
}
}
function keypointsCompatible(keypoint_1, keypoint_2) {
var outcome = true;
if (activeParts.length > 0) {
compatibility_loop: for (i = 0; i < activeParts.length; i++) {
if (Object.values(activeParts[i]).includes(keypoint_1) && Object.values(activeParts[i]).includes(keypoint_2)) {
console.log(keypoint_1 + " and " + keypoint_2 + " are not compatible because they already exist as " + activeParts[i].point_1 + " and " + activeParts[i].point_2 + " respectively");
outcome = false;
break compatibility_loop;
console.log("Compatibility NOT broken!!");
}
}
}
console.log("Compatibility outcome is " + outcome);
return outcome;
}
The code is suppose to take two values in the same array and compare them. If a number of conditions are met, including if they're a certain distance apart from one another, they will be pushed into a secondary array. If the values already appear in the secondary array, which the keypointCompatible function is suppose to determine, the loop should either continue looking for other candidates or stop before being called again. For some reason, however, the code is getting stuck within the keypointCompatible function when it detects that the values have already appeared in the secondary array and the console will repeatedly print "Compatibility is false" until the browser crashes.
Working Solution
Use let or const instead of var or nothing. Your issue may be related to closures and variables reused between loops. Make sure you use let or const in your loops too. for (let i=0).
When you use let or const, the runtime will create a new instance every time the block or loop iterates. However, using var will reuse the internal allocation.
So what happens with the standard var is the multiple closures or loops each use the same instance of the variable.
Unless you want the var behavior, always use let or const.
Another Solution
Put a newline after the label compatibility_loop
Still Another Solution
The first function is pushing into activeParts. The second function is looping activeParts. This can go on forever, or longer than expected. Pushing into the array could possibly make the loop limit never reached.
Put a log on the length of activeParts in the second function to see if it is growing out of control.
Your code should be OK if varifiedKeypoints.length has reasonable value. And all internal variables are declared properly!
You have two loops (this inner can start at j=i+1 to save time and multiple calculations) with few conditions inside.
function compareKeypoints(varifiedKeypoints) {
outer_loop: for (let i = 0; i < varifiedKeypoints.length; i++) {
let initialKeypoint = varifiedKeypoints[i];
for (let j = i+1; j < varifiedKeypoints.length; j++) {
let comparisonKeypoint = varifiedKeypoints[j];

Explanation How Rectangle Recursion JavaScript works

I am a beginner in code world. I have troubles understanding recursion in JavaScript especially when it needs two or more looping. Like I want to print rectangle using recursion. I don't know completely how to make a base case, condition when it still executed. For examples, these codes below I use to print rectangle or holey rectangle.
function box(num) {
for (let i = 0; i < num; i++) {
let str = ''
for (let j = 0; j < num; j++) {
str += '*'
}
console.log(str)
}
}
box(5)
function holeBox (num) {
for(let i = 0; i < num; i++){
let str = ''
for(let j = 0; j < num; j++){
if(i == 0 || i == num -1 || j == 0 || j == num - 1) {
str += '*'
} else {
str += ' '
}
}
console.log(str)
}
}
holeBox (5)
Please help me to understand recursion, an explanation would be great. My goals are not only to solve those codes but also to understand how recursion works. I've searched there's no good source to learn recursion, or I just too dumb to understand. Thanks in advance
To understand how recursion works, just think of how you can split up what you want to accomplish into smaller tasks, and how the function can complete one of those tasks, and then call itself to do the next- and so on until it is finished. I personally don't think printing boxes is the best way to learn recursion, so imagine you wanted to search an array for a specific value; ignore JavaScript's indexOf()/find() functions or similar for now.
To do this using loops, its easy, just iterate over the array, and check every value:
//Returns the index of the first occurrence of a value in an array, or -1 if nothing is found
function search(needle, haystack) {
for (let i = 0; i < haystack.length; i++) {
if (haystack[i] == needle) return i;
}
return -1;
}
Doing this using recursion is easy as well:
function recursiveSearch(needle, haystack, i) {
if (i > (haystack.length - 1)) return -1; //check if we are at the end of the array
if (haystack[i] == needle) return i; //check if we've found what we're looking for
//if we haven't found the value yet and we're not at the end of the array, call this function to look at the next element
return recursiveSearch(needle, haystack, i + 1);
}
These functions do the same thing, just differently. In the recursive function, the two if statements are the base cases. The function:
Tests if the current element is out of bounds of the array (meaning we've already searched every element), and if so, returns -1
Tests if the current element is what we're looking for, and if so, returns the index
If neither of the statements above apply, we call this function recursively to check the next element
Repeat this until one of the base cases kicks in.
Note that recursive functions are usually called from other helper functions so that you don't have to pass the initial parameters to call the function. For example, the recursiveSearch() function above would be private, and it would be called by another function like this:
function search(needle, haystack) {
return recursiveSearch(needle, haystack, 0);
}
so that we don't have to include the third parameter when we call it, thus decreasing confusion.
Yes, even your box code can be turn into recursion but I don't think it will help you understand the concept of recursion.
If you really have to:
function getBox(arr, size) {
let length = arr.length;
if (length == size)
return arr; // recursion stop rule - if the size reached
for (let i = 0; i < length; i++)
arr[i].push("*"); // fill new size for all row
arr.push(new Array(length + 1).fill("*")); // add new row
return getBox(arr, size); // recursive call with arr bigger in 1 row
}
However, I believe #Gumbo answer explain the concept better then this...

JavaScript: setInterval and for loop explanation

i searched around for a couple of questions related to the use of the for loop and the setInterval function in JavaScript but i couldn´t find a concrete answer on why this snippet doesn´t work. Could someone explain please what´s happening under the hood and why this code doesn´t print anything at all?
for (let i = 0; i++; i < 10) {
window.setInterval(function () {
console.log('Test');
} , 100)
}
Your for loop is not correct. The condition needs to be the second statement in the for loop.
Following code should work.
for (let i = 0; i < 10 ; i++; ) {
window.setInterval(function () {
console.log('Test');
} , 100)
}
Expected Syntax for loop. You can read more here
for ([initialization]; [condition]; [final-expression])
statement
EDIT 1:
Though all answers (including mine) mentioned that condition needs to be second statement in the for loop which is correct. There is one more additional important behavior.
The for loop for (let i = 0; i++; i < 10) is actually correct in terms of grammar and even the javascript runtime executes this code.
But, as in your case, if the condition is evaluating to falsy value then it would exit the loop.
Breaking your for loop for (let i = 0; i++; i < 10) into each seperate construct
Initialization: let i = 0; This statement initializes the value of variable i to 0.
Condition: i++; Javascript evaluates the statement to check if the statement is true or not. In case of i++ the runtime firstly checks for the current value of i which is 0 . Since 0 is considered a falsy value the condition evaluates to false and hence the statement is not executed. Also, i++ statement is a post increment which basically increments i and then result the original value of i.
So, if you would have written loop like below, using the intiliaztion of i=1, then it would have worked, though it would be running infinitely untill the browser/Server crashes as the condition i++ would always evaluate to true. I hope that makes sense.
for (let i = 1; i++; i < 10) {
// Statements would run
}
Or
for (let i = 0; ++i; i < 10) { **// Pre increment**
// Statements would run
}
Or
for (let i = 0; i=i+1; i < 10) { **// increment i by assigment
// Statements would run
}
Douglas Crockford in his book Good Parts mention about the usage of ++ & -- and how it can confuse readers.
your for loop syntax is wrong, should be
for (let i = 0; i < 10; i++)
your setInterval code will run every 100 milliseconds for each iteration of the loop (so 10 times every 100 milliseconds)
Nothing to do with setInterval, you simply malformed your for loop:
This:
for (let i = 0; i++; i < 10)
Should be this:
for (let i = 0; i < 10; i++)
First declare the initial state of the loop, then the terminating state of the loop, then the incremental change of the loop.
Observe.

Counting how many times a function has been called recursivly in JS

I have got a homework where i am supposed to write a pseudo random number generator in JavaScript. This is the code bit i wrote
var k = 0;
var slump = function(n, k) {
if (k < 10) {
console.log("stop");
}
else {
k++;
console.log((5*n + 1) % 8);
return slump((5*n + 1) % 8, k);
}
};
slump(0);
k is supposed to hold the amount of times the function has been called. But instead of just running the function ten times, it just keeps running. Is there any way to get around this?
You have two subtly different options here, depending on how idiomatic and clever you'd like to get.
The classic implementation, with a slight tweak as JS doesn't support default parameters, would be to use something like:
var finalDepth = 0;
function slump(n, k) {
k = k || 0; // Set to 0 if falsy (null, undef, or 0)
if (logic) {
finalDepth = k; // Record the depth on the last call
} else {
return slump((5*n + 1) % 8, k + 1);
}
}
This will very simply record the deepest the stack has been, and hang onto the value until the next call.
If you want to be slightly more JS-like, you can use closure to keep track of the calls:
function createGenerator() {
var counter = 0;
return {
slump: function (n) {
++counter; // Closure captures counter, counter persists between slump calls but is unique for each createGenerator
if (logic) {
// stop
} else {
return slump((5*n + 1) % 8, k + 1);
}
},
getCounter: function () { return counter; }
}
}
You may be able to use some of the features from ES6 iterators (or generators) to make this more clever.
The function parameter k is uninitialized, therefore not a number. this means in particular that the termination test k < 10 fails as well as the k++ statement doesn't change k' s value. so slump gets always called with the same value for parameter k and the recursion never stops.
Whenever you write a recursive function, you need to make sure that:
There's a base case (in your case, when the console.log statement runs)
The function proceeds towards the base case, and
The function works, assuming the success of the recursive call.
You're running into a problem with the second part; you increment k, but that doesn't bring you any closer to the part where k < 10. In short, you probably want to switch that test around and make sure you're initially calling the function with the right number of arguments. (Aadit M Shah pointed out that you're calling it with one, and it expects two, meaning that it ends up undefined when you call it.)
Either way, iteration would definitely work better here:
var n = 0;
for(var i = 0; i < 10; i++) {
n = (5 * n + 1) % 8;
console.log(n);
}

Why aren't my ball (objects) shrinking/disappearing?

http://jsfiddle.net/goldrunt/jGL84/42/
this is from line 84 in this JS fiddle. There are 3 different effects which can be applied to the balls by uncommenting lines 141-146. The 'bounce' effect works as it should, but the 'asplode' effect does nothing. Should I include the 'shrink' function inside the asplode function?
// balls shrink and disappear if they touch
var shrink = function(p) {
for (var i = 0; i < 100; i++) {
p.radius -= 1;
}
function asplode(p) {
setInterval(shrink(p),100);
balls.splice(p, 1);
}
}
Your code has a few problems.
First, in your definition:
var shrink = function(p) {
for (var i = 0; i < 100; i++) {
p.radius -= 1;
}
function asplode(p) {
setInterval(shrink(p),100);
balls.splice(p, 1);
}
}
asplode is local to the scope inside shrink and therefore not accessible to the code in update where you are attempting to call it. JavaScript scope is function-based, so update cannot see asplode because it is not inside shrink. (In your console, you'll see an error like: Uncaught ReferenceError: asplode is not defined.)
You might first try instead moving asplode outside of shrink:
var shrink = function(p) {
for (var i = 0; i < 100; i++) {
p.radius -= 1;
}
}
function asplode(p) {
setInterval(shrink(p),100);
balls.splice(p, 1);
}
However, your code has several more problems that are outside the scope of this question:
setInterval expects a function. setInterval(shrink(p), 100) causes setInterval to get the return value of immediate-invoked shrink(p). You probably want
setInterval(function() { shrink(p) }, 100)
Your code for (var i = 0; i < 100; i++) { p.radius -= 1; } probably does not do what you think it does. This will immediately run the decrement operation 100 times, and then visually show the result. If you want to re-render the ball at each new size, you will need to perform each individual decrement inside a separate timing callback (like a setInterval operation).
.splice expects a numeric index, not an object. You can get the numeric index of an object with indexOf:
balls.splice(balls.indexOf(p), 1);
By the time your interval runs for the first time, the balls.splice statement has already happened (it happened about 100ms ago, to be exact). I assume that's not what you want. Instead, you should have a decrementing function that gets repeatedly called by setInterval and finally performs balls.splice(p,1) after p.radius == 0.
setInterval(shrink(p),100);
This doesn't do what you think it does. This calls shrink, passes it p, and then passes the result to setInterval. shrink(p) returns undefined, so this line doesn't actually put anything on an interval.
You probably want:
setInterval(function(){
shrink(p)
}, 100);

Categories

Resources