Recursive functions in Javascript and depth-tracking - javascript

I'm writing a recursive function in JS and having some trouble. Let's start with this very basic function:
function traverse(thing)
{
if (typeof traverse.depth == 'undefined')
traverse.depth = 1;
else
traverse.depth ++;
if (thing.child)
traverse(thing.child);
}
So this works fine, and depth acts as a static var of sorts, but the problem is that in a language like C that has proper static vars, as you back out of the function, this variable would (ostensibly) decrease, so it is a true depth. If I have three boxes that contain three boxes and each of those contain three boxes, etc., we're essentially drilling down into the deepest one till there are no children, then backing out up a level to a sibling, and traversing its children. The problem with the above code is that the depth keeps increasing and increasing infinitely, even though the TRUTH depth may only be 3 or 4 from the oldest ancestor to the youngest child. If there are 80 siblings on each level, that depth counter is going to skyrocket.
How do I keep track of true depth in JS recursive functions?

Don't attach the counter to the function. The counter is shared by all recursive calls, so the counter represents the number of function calls, instead of the recursion depth.
When depth is passed as a separate variable, the counter shows the true depth.
function traverse(thing, depth)
{
if (typeof depth == 'number')
depth++;
else
depth = 1;
if (thing.child)
traverse(thing, depth);
}

Another (and perhaps nicer) solution would be to utilize JS functional programming strengths and use a high-order function to keep all depth-related housekeeping outside of the main function. Consider, for example, the following classic example:
function fac(n) {
if(n < 3)
return n;
return n * fac(n - 1);
}
We want this one to break the recursion once its goes deeper than a given value. Let's code a wrapper:
function wrapDepth(fn, max) {
var depth = 0
return function() {
if (++depth > max)
throw "Too much recursion"
var out = fn.apply(null, [].slice.call(arguments, 0))
depth--;
return out;
}
}
Create a wrapper with max depth = 20:
fac = wrapDepth(fac, 20)
and test:
console.log(fac(10)) // 3628800
console.log(fac(100)) // Too much recursion
Note that we didn't make any change in the main function fac itself, but still, its recursion depth is now under control.

why don't you just modify the function signature to take a thing and an index? So you would call it like:
function traverse(thing, idx)
...
if (condition)
traverse(thing.child, ++idx)

Just add:
traverse.depth--;
Right before any return statements. So
if(x === 5)
return thing;
Would become:
if(x === 5){
traverse.depth--;
return thing;
}
And then add traverse.depth--; before your closing } of the function.

If I understood correctly, it looks to me like you want to track the depth of recursion.. but in your code you never decrement the depth as you finish 1 level in the recursion.
I tried out a simple code and I think this what you wanted,
DEMO
HTML:
<div id="result"></div>
JS:
var result = document.getElementById('result');
var tmpArray = [1,2,3,4,5,6,7,8];
function depthTest() {
if (typeof depthTest.depth == 'undefined')
depthTest.depth = 0;
else
depthTest.depth++;
result.innerHTML += depthTest.depth;
if (typeof tmpArray[depthTest.depth] != 'undefined')
depthTest();
result.innerHTML += depthTest.depth;
depthTest.depth--;
}
depthTest(tmpArray);
OUTPUT:
012345678876543210

Related

Why is a recursive deep equality check faster than an iterative version in Javascript?

I noticed that all the deep equality implementations I've found are using recursion and theoretically the iterative form should be faster. However, it's a bit slower for me and I don't understand why.
Assume the data is the result of JSON.parse (i.e. primitives, plain objects, and arrays).
Recursive:
function equals1(x, y) {
if (x === y) return true;
if (Array.isArray(x) && Array.isArray(y)) {
if (x.length !== y.length) return false;
for (let i = 0; i < x.length; i++) {
if (!equals1(x[i], y[i])) return false;
}
return true;
}
if ((typeof x !== 'object') || (typeof y !== 'object')) return false;
const xKeys = Object.keys(x);
const yKeys = Object.keys(y);
if (xKeys.length !== yKeys.length) return false;
for (const k of xKeys) {
if (!y.hasOwnProperty(k)) return false;
if (!equals1(x[k], y[k])) return false;
}
return true;
}
Iterative:
function equals2(a, b) {
const stack = [a, b];
let idx = 2;
while (idx > 0) {
const x = stack[idx - 1];
const y = stack[idx - 2];
idx -= 2;
if (x === y) continue;
if (Array.isArray(x) && Array.isArray(y)) {
if (x.length !== y.length) return false;
for (let i = 0; i < x.length; i++) {
idx += 2;
if (idx > stack.length) stack.push(x[i], y[i]);
else {
stack[idx - 1] = x[i];
stack[idx - 2] = y[i];
}
}
} else {
if ((typeof x !== 'object') || (typeof y !== 'object')) return false;
const xKeys = Object.keys(x);
const yKeys = Object.keys(y);
if (xKeys.length !== yKeys.length) return false;
for (const k of xKeys) {
if (!y.hasOwnProperty(k)) return false;
idx += 2;
if (idx > stack.length) stack.push(x[k], y[k]);
else {
stack[idx - 1] = x[k];
stack[idx - 2] = y[k];
}
}
}
}
return true;
}
I'm using the index instead of the traditional stack.pop approach because it's slightly faster.
JSPerf: https://jsperf.com/deep-object-compare-123/1
The data is from Reddit: https://www.reddit.com/r/javascript.json
For me, the iterative version is 20-25% slower on Chrome and Edge, and the same speed on Firefox. I tried pre-allocating the stack array and removing the continue, but it didn't change the results. As far as I know, JS engines can optimize tail-recursive functions, but this isn't tail-recursive.
Any ideas what's going on?
A major difference between the two approaches is that your recursive function is doing a normal depth-first search for the first unequal value while your iterative function is putting all the children of an array/object onto the stack before searching into last child. This causes the stack array to grow much larger than the call stack of the recursive function will ever become, and it does quite some unnecessary copying of the entire data structure into a heterogenous array instead of keeping the values in local variables.
You do a single test on one single data structure, and from that you mean you can conclude that in general, recursive equality checks are faster than iterative ones? Are you serious? From that one single test, you can conclude nothing whatsoever. Neither could I conclude (anything much) of any one test where some iterative algorithm wins. There are sooooooooooooo many recursive and iterative ways to do deep equality testing (and other things), and there are sooooooooo many data structures. I did more than one test, and my results are, very decisively: INCONCLUSIVE, see below.
But first, one thing:
There is a little bug in your code: you don't properly check for the null case. If one argument is null, and the other one is a non-null object, an error will be thrown.
That can be easily fixed by adding the following line:
if ((x===null)||(y===null)) return false;
In equals1, put it right after if (x === y) return true;, and in equals2 put it after the continue. If both arguments were null, then the line before the inserted line does the right thing and makes the code not reach the inserted line, and if only one of the arguments is null, then the inserted line will take care of false being returned, instead of an error being thrown.
I have to admit that I find your iterative version very hard to read. I just can't understand it, but very much would like to. How does it work? Could you enlighten us, please? Does it use LIFO (last in first out) stack, corresponding to depth first search, or is it based on something different? I really would like to know.
I wrote another iterative version, based on your equals1, using a FIFO queue (first in first out, corresponding to breadth first search) - I find that very much easier to read.
And I added 3 test cases to jsperf.com, here they are:
redditData
linkedList1Knodes
linkedList10Knodes
All 3 tests use your equals1 and equals2 you quoted here (with the null bug fixed) and the FIFO version I wrote.
First test uses the original reddit data from your question, second uses a linked list with 1 thousand nodes, and third uses a linked list with 10 thousand nodes.
First test confirms that that your iterative version is about 20% slower than the recursive version, and my FIFO version is in between the two, at about 10% slower than the recursive version.
In the second test, your iterative version is the clear winner, it is much faster than the recursive version, and FIFO comes in last (it's a teeny wee bit slower than the the recursive).
In the third test, the recursive version crashes - stack overflow error, and again your iterative version is the winner (FIFO being about 30% slower)
Sorry I can't explain to you why all that is. A proper explanation probably would have to shed light onto many different aspects, I don't think there is a "singular elephant" explaining it all; maybe it makes sense to add more heterogeneous test cases, instead just one example from reddit (even if that is "real world"...)
And here is the FIFO iterative version
function equals_FIFO(x, y){
if (x===y) return true;
if ((x===null)||(y===null)||((typeof x)!=='object')||((typeof y)!=='object')) return false;
var xStack = [x], yStack = [y];
var currentIdx = 0;
var item1, item2, kid1, kid2, keys1, keys2, i, key;
while (currentIdx<xStack.length){
item1 = xStack[currentIdx];
item2 = yStack[currentIdx];
keys1 = Object.keys(item1);
keys2 = Object.keys(item2);
if (keys1.length!==keys2.length) return false;
for (i=0; i<keys1.length; i++){
key = keys1[i];
if (!item2.hasOwnProperty(key)) return false;
kid1 = item1[key];
kid2 = item2[key];
if (kid1!==kid2){
if ((kid1===null)||(kid2===null)||((typeof kid1)!=='object')||((typeof kid2)!=='object')) return false;
xStack.push(kid1);
yStack.push(kid2);
}
}
currentIdx++;
}
return true;
}

Fibonacci for large numbers in Javascript

I have the following code:
function fib(n) {
let first=BigInt(0);
let snd=BigInt(1);
let currentNumber;
let countMax=Math.abs(n)+1;
let counter=2;
if(n==0){
return first;
}
else if (n==1||n==-1){
return snd;
}
while(counter<countMax)
{
currentNumber=first+snd;
first=snd;
snd=currentNumber;
counter++;
}
if((n<0) && (n % 2 ==0))
{
return -currentNumber;
}
return currentNumber;
}
That returns the fibonacci number for the given (n).
My issue is that I have to improve the performance of this code. I tried to use different fibonacci formulas (exponential ones) but I lose a lot of precision cause phi number has infinite decimals, so I have to truncate and for big numbers I lost a lot of precision.
When I execute for instance fib(200000) I get the huge number but the code spends more than 12000 ms.
For other hand I tried using recursion but the performance decreases.
Could you provide me an article or clue to follow?
Thanks & Regards.
First of all, you can refer the answer here which says that
Fib(-n) = -Fib(n)
Here's the recursive implementation which is not efficient as you mentioned
function fib(n) {
// This is to handle positive and negative numbers
var sign = n >= 0 ? 1 : -1;
n = Math.abs(n);
// Now the usual Fibonacci function
if(n < 2)
return sign*n;
return sign*(fib(n-1) + fib(n-2));
}
This is pretty straightforward and I leave it without explaining because if you know Fibonacci series, you know what the above code does. As you already know, this is not good for very large numbers as it recursively calculate the same thing again and again. But we'll use it in our approach later on.
Now coming towards a better approach. See the below code similar to your code just a bit concise.
function fib(n) {
if(n == 0)
return 0;
var a = 1;
var b = 1;
while(n > 2) {
b = a + b;
a = b - a;
}
// If n is negative then return negative of fib(n)
return n < 0 ? -1*b : b;
}
This code is better to use when you want to call this function only a few times. But if you want to call it for frequently, then you'll end up calculating the same thing many times. Here you should keep track of already calculated values.
For example, if you call fib(n) it will calculate nth Fibonacci number and return it. For the next time if you call fib(n) it will again calculate it and return the result.
What if we store this value somewhere and next time retrieve it whenever required?
This will also help in calculating Fibonacci numbers greater than nth Fibonacci number.
How?
Say we have to calculate fib(n+1), then by definition fib(n+1) = fib(n) + fib(n-1). Because, we already have fib(n) calculated and stored somewhere we can just use that stored value. Also, if we have fib(n) calculated and stored, we already have fib(n-1) calculated and stored. Read it again.
We can do this by using a JavaScript object and the same recursive function we used above (Yes, the recursive one!). See the below code.
// This object will store already calculated values
// This should be in the global scope or atleast the parent scope
var memo = {};
// We know fib(0) = 0, fib(1) = 1, so store it
memo[0] = 0;
memo[1] = 1;
function fib(n) {
var sign = n >= 0 ? 1 : -1;
n = Math.abs(n);
// If we already calculated the value, just use the same
if(memo[n] !== undefined)
return sign*memo[n];
// Else we will calculate it and store it and also return it
return sign*(memo[n] = fib(n-1) + fib(n-2));
}
// This will calculate fib(2), fib(3), fib(4) and fib(5).
// Why it does not calculate fib(0) and fib(1) ?
// Because it's already calculated, goto line 1 of this code snippet
console.log(fib(5)); // 5
// This will not calculate anything
// Because fib(-5) is -fib(5) and we already calculated fib(5)
console.log(fib(-5)); // -5
// This will also not calculate anything
// Because we already calculated fib(4) while calculating fib(5)
console.log(fib(4)); // 3
// This will calculate only fib(6) and fib(7)
console.log(fib(7)); // 13
Try out some test cases. It's easy to understand why this is faster.
Now you know you can store the already calculated values, you can modify your solution to use this approach without using recursion as for large numbers the recursive approach will throw Uncaught RangeError. I leave this to you because it's worth trying on your own!
This solution uses a concept in programming called Dynamic Programming. You can refer it here.
If you just add the previous value to the current one and then use the old current value as the previous one you get a significant improvement in performance.
function fib(n) {
var current = 1;
var previous = 0;
while (--n) {
var temp = current;
current += previous;
previous = temp;
}
return current;
}
console.log(fib(1)); // 1
console.log(fib(2)); // 1
console.log(fib(3)); // 2
console.log(fib(4)); // 3
console.log(fib(5)); // 5
You can also use an array in the parent scope to store the previous values to avoid redoing the same calculations.
var fibMap = [1, 1];
function fib(n) {
var current = fibMap[fibMap.length - 1];
var previous = fibMap[fibMap.length - 2];
while (fibMap.length < n) {
var temp = current;
current += previous;
previous = temp;
fibMap.push(current);
}
return fibMap[n - 1];
}
console.log(fib(1)); // 1
console.log(fib(2)); // 1
console.log(fib(3)); // 2
console.log(fib(4)); // 3
console.log(fib(5)); // 5
Benchmark for getting the 1000th number 3 times

RangError: too many arguments provided for a function call

I got a nice solution to get HTML Comments from the HTML Node Tree
var findComments = function(el) {
var arr = [];
for (var i = 0; i < el.childNodes.length; i++) {
var node = el.childNodes[i];
if (node.nodeType === 8) {
arr.push(node);
} else {
arr.push.apply(arr, findComments(node));
}
}
return arr;
};
var commentNodes = findComments(document);
// whatever you were going to do with the comment...
console.log(commentNodes[0].nodeValue);
from this thread.
Everything I did was adding this small loop to print out all the nodes.
var arr = [];
var findComments = function(el) {
for (var i = 0; i < el.childNodes.length; i++) {
var node = el.childNodes[i];
if (node.nodeType === 8) {
arr.push(node);
} else {
arr.push.apply(arr, findComments(node));
}
}
return arr;
};
var commentNodes = findComments(document);
//I added this
for (var counter = arr.length; counter > 0; counter--) {
console.log(commentNodes[counter].nodeValue);
}
I keep getting this Error Message:
RangeError: too many arguments provided for a function call debugger
eval code:9:13
EDIT: i had a typo while pasting changed the code from i-- to counter--
see this comment in MDN docs about the use of apply to merge arrays:
Do not use this method if the second array (moreVegs in the example) is very large, because the maximum number of parameters that one function can take is limited in practice. See apply() for more details.
the other note from apply page:
But beware: in using apply this way, you run the risk of exceeding the JavaScript engine's argument length limit. The consequences of applying a function with too many arguments (think more than tens of thousands of arguments) vary across engines (JavaScriptCore has hard-coded argument limit of 65536), because the limit (indeed even the nature of any excessively-large-stack behavior) is unspecified. Some engines will throw an exception. More perniciously, others will arbitrarily limit the number of arguments actually passed to the applied function. To illustrate this latter case: if such an engine had a limit of four arguments (actual limits are of course significantly higher), it would be as if the arguments 5, 6, 2, 3 had been passed to apply in the examples above, rather than the full array.
As the array start from index of 0, actually the last item in the array is arr.length - 1.
you can fix it by:
for (var counter = arr.length - 1; counter >= 0; counter--)
Notice I've added arr.length -1 and counter >= 0 as zero is the first index of the array.
Adding the for loop is not the only thing you changed (and see the other answer about fixing that loop too). You also moved the declaration of arr from inside the function to outside, making arr relatively global.
Because of that, each recursive call to findComments() works on the same array, and the .apply() call pushes the entire contents back onto the end of the array every time. After a while, its length exceeds the limit of the runtime.
The original function posted at the top of your question has arr declared inside the function. Each recursive call therefore has its own local array to work with. In a document with a lot of comment nodes, it could still get that Range Error however.

Bubble sort not swapping elements of array in Javascript

I am creating a simple program that should utilize the bubble sort algorithm to sort a list of numbers in ascending order.
Just for testing purposes I have added the line alert(unsortedNumbers);and as you can see if you run it, the numbers do not change order no matter how many passes the algorithm does.
The program seems to be stuck in an infinite loop, as 'Another pass' is printed to the console repeatedly. As instructed by this line console.log("Another pass");
As with the bubble sort algorithm, once it does not have to swap any terms on a certain pass, we know this is the sorted list, I have created the variable swapped, however it looks like this is always 1. I think this may be caused by the swapArrayElements() function not swapping the terms.
Why is the function not swapping the index of the terms within the array?
(Code does't seem to run properly on SO's code snippet tool, may have to copy into notepad document)
function main(){
var unsortedNumbers =[7,8,13,1,6,9,43,80]; //Declares unsorted numbers array
alert(unsortedNumbers);
var swapped = 0;
var len = unsortedNumbers.length;
function swapArrayElements(index_a, index_b) { //swaps swapArrayElements[i] with swapArrayElements[ii]
var temp = unsortedNumbers[index_a];
unsortedNumbers[index_a] = unsortedNumbers[index_b];
unsortedNumbers[index_b] = temp;
}
function finish(){
alert(unsortedNumbers);
}
function mainBody(){
for(var i =0;i<len;i++){
var ii =(i+1);
if (unsortedNumbers[i]>unsortedNumbers[ii]){
console.log("Swap elements");
swapArrayElements(i,ii);
swapped=1; // Variable 'swapped' used to check whether or not a swap has been made in each pass
}
if (ii = len){
if (swapped = 1){ // if a swap has been made, runs the main body again
console.log("Another pass");
alert(unsortedNumbers); //Added for debugging
swapped=0;
mainBody();
}else{
console.log("Finish");
finish();
}
}
}
}
mainBody();
}
<head>
</head>
<body onload="main()">
</body>
You have an error in your code:
if (ii = len) {
and also
if (swapped = 1){
it should be double equal
Invalid condition check causing infinite loop:
if (ii = len) & if (swapped = 1) should have == or === operator. This is causing infinity loop.
NOTE: Your code is not appropriate as per the best practices to avoid global variables. You should not use global variables and try
passing variables and returning them back after processing.
Refer this for avoiding globals.

Trouble pushing to an array in JS

Below is just a section of my code but I know it's problematic because I can't get it to return any value except 'undefined'. I have been over this for hours and cannot figure it out.
I want to be able to input a number and have its factors pushed to an array. I have tested it by alerting the first item in the array and I get nothing. I'm sure this is a pretty easy but I just can't figure it out. Here is the code:
var numberInQuestion = prompt("Of what number are you wanting to find the largest prime factor?");
//determine factors and push to array for later use
var factorsArray = [];
function factors(numberInQuestion){
for(var i = 2; i < numberInQuestion-1; i++){
if(numberInQuestion % i === 0){
return factorsArray.push[i];
} else {
continue;
}
}
};
factors(numberInQuestion);
alert(factorsArray[0]);
Thanks for any help!
you can only return one value
you must use (), not [] for calling push
factorsArray should be local to factors (put the definition inside the function)
the else { continue; } is useless
Here is the fully corrected code:
var numberInQuestion = prompt("Of what number are you wanting to find the factors of?");
//determine factors
function factors(numberInQuestion){
var factorsArray = []; // make it local
for (var i = 2; i < numberInQuestion-1; i++){
if(numberInQuestion % i === 0){
factorsArray.push(i); // use (), and don't return here
} // no need for else { continue; } because it's a loop anyway
}
return factorsArray; // return at the end
};
var result = factors(numberInQuestion); // assign the result to a variable
alert(result);
Here's a JSFiddle.
You have an error in your pushing syntax. Correct syntax for pushing is -
factorsArray.push(i);
Also returning immediately from the function after finding the first divisor will not give you the full list. You probably want to return after you've found out all the divisors.
Taking all of the above into consideration, you should rewrite your function as follow -
function factors(numberInQuestion){
for(var i = 2; i < numberInQuestion - 1; i++){
if(numberInQuestion % i === 0) {
factorsArray.push(i);
}
}
}
and you will be OK.
You've coded this so that when you find the first factor your function returns immediately. Just get rid of the return keyword in that statement. (What "return" means in JavaScript and other similar languages is to immediately exit the function and resume from where the function was called.)
Oh, also, you call functions (like .push()) with parentheses, not square brackets.
The function should not return when pushing to the array. Return the array after executing the loop. The else clause is also unnecessary.
var numberInQuestion = prompt("Of what number are you wanting to find the largest prime factor?");
function factors(numberInQuestion){
var factorsArray = [];
for(var i = 2; i < numberInQuestion-1; i++){
if(numberInQuestion % i === 0 && isPrime(i)){
factorsArray.push(i);
}
}
return factorsArray;
};
var factors = factors(numberInQuestion);
alert(factors[factors.length-1]);
//From: http://stackoverflow.com/questions/11966520/how-to-find-prime-numbers
function isPrime (n)
{
if (n < 2) return false;
var q = Math.sqrt (n);
for (var i = 2; i <= q; i++)
{
if (n % i == 0)
{
return false;
}
}
return true;
}
Given the purpose of the example two items must be considered
The code does not determine if the number is actually prime. The code will return the smallest factor possible since the loop starts at two and increments, then returns the first element in the array. The largest factor would actually be the last element in the array. I have corrected the example to find the greatest prime factor. You can test it via this fiddle: http://jsfiddle.net/whKGB/1/

Categories

Resources