Trying to understand recursion within for loops in javascript - javascript

I've been staring at the answer to this question forever even writing down variables and whatnot through each iteration. I simply just don't get the process here. When I throw in console logs I see that permute is called input.length - 1 times before it ever gets to this line input.splice(i, 0, ch); It's hard to phrase the question when I am lost completely, but I guess some curiosities are: each time permute is called, it is a new instance of that function with it's own closure right? therefore variables changes that are within the function won't affect variables in other calls? does the function return permArr for each time it's called? and I suppose that doesn't necessarily affect the return of the first call? (my instinct tells me that the first time return happens, the function stops running).
Thanks for the insight.
Permutations in JavaScript?
var permArr = [],
usedChars = [];
function permute(input) {
var i, ch;
for (i = 0; i < input.length; i++) {
ch = input.splice(i, 1)[0];
usedChars.push(ch);
if (input.length == 0) {
permArr.push(usedChars.slice());
}
permute(input);
input.splice(i, 0, ch);
usedChars.pop();
}
return permArr
};

I'll give this a shot.
Overview
You start with two arrays that will have global scope: permArray will eventually hold all of the permutation arrays, and usedChars is a worker array used build each individual permutation array through all of the recursive calls. It's important to note that these are the only two variables that are accessible in the scope of every function created. All other variables have local scope to their own function call.
Then there is the recursive function which accepts an array as input and returns an array of array with all possible permutations of the input array. Now, in this particular function the recursive call is inside a loop. This is interesting because the terminating condition is actually more complicated than your basic recursive function--the recursive calls terminate when you pass in an empty input array and the for loop skips over the next recursive call.
Summary
Consider a four element array input. At a high level, the function is going to loop over the four elements of this array, pull out each element, and compute the permutation of that smaller array of three elements. With all of those three element permutations, it will append the original element pulled out to the beginning and add each of those four element arrays to permArray.
But, in order to find the permutation of the smaller three element arrays, we pull out each element, compute the permutation of that smaller array of two elements, add the element pulled out to the beginning of each of those permutations, and return each of those three element arrays up the recursion call stack so the original fourth element can be added to the beginning and counted as a permutation.
But, in order to find the permutation of the smaller two element arrays, we pull out each element, compute the permutation of that smaller array of one element, add the element pulled out the the beginning of each of those permutations, and return each of those two element arrays up the recursion call stack so the original third element can be added to the beginning of that permutation and returned up the stack.
But, in order to find the permutation of the smaller one element array, we pull out the element and compute the permutation of that empty array, which just returns, and we in turn just return our one element back up the stack so the original second element can be added to the beginning of that permutation and returned up the stack.
Details
Let's note some of the steps in this function:
var permArr = [],
usedChars = [];
function permute(input) {
var i, ch;
for (i = 0; i < input.length; i++) { // loop over all elements
ch = input.splice(i, 1)[0]; //1. pull out each element in turn
usedChars.push(ch); // push this element
if (input.length == 0) { //2. if input is empty, we pushed every element
permArr.push(usedChars.slice()); // so add it as a permutation
}
permute(input); //3. compute the permutation of the smaller array
input.splice(i, 0, ch); //4. add the original element to the beginning
// making input the same size as when we started
// but in a different order
usedChars.pop(); //5. remove the element we pushed
}
return permArr //return, but this only matters in the last call
};
Let's trace through the details using the array [4,3,2,1].
When it's first passed in, we'll take out the 4, push it to usedChars, remove it from input, and call permute on [3,2,1]. In this call, we'll push 3 to usedChars, remove it from input, and call permute on [2,1]. Then we push 2 to usedChars, remove it from input, and callpermuteon [1]. Then we push 1 tousedChars, and remove it frominput`.
This leaves us four calls deep and at step (2) with:
ch=1
input=[]
usedChars=[4,3,2,1]
At step (2), we're going to push our first permutation [4,3,2,1] to permArr. Then, moving on, since input is now empty the recursive call in (3) will simply return and in (4) we will simply add 1 back into input and remove 1 from usedChars--and this call returns.
So, at this point, we have started backing up our recursive calls and sit at step (4) with:
ch=2
input=[1]
usedChars=[4,3,2]
Step (4) will perform the critical step of the algorithm: the moving action. It takes ch=2 and adds it to the beginning of input and removes it from usedChars. This means that after step (5), we have:
ch=2
input=[2,1]
usedChars=[4,3]
Now take a look at where we are. We pushed [4,3,2,1] as a permutation, then backed up and swapped 2 and 1, and now we're going to go back into recursive calls to build [4,3,1,2] and add it as a permutation. After that we will back out some more, move around some more elements, and go back into the permutations and add them.
Getting back into it, after step (5) is executed we loop. That means we will push 1 to usedChars and make a recursive call with input=[2]. That call will push 2 into usedChars creating a the full array [4,3,1,2] and causing it to be added to permArray.
So, you're going to cycle up and down through the recursive calls building up a permutation, backing back out, rebuilding a different permutation, and backing out until you've looped over every possible combination.
I hope this helps!

The code is a bit hard to follow, because it's a mix of looping and recursion. It uses a global variable (usedChars) that is changed and restored during each call.
"each time permute is called, it is a new instance of that function
with it's own closure right? therefore variables changes that are
within the function won't affect variables in other calls?"
Well, yes and no. Each function call has its own scope, but as there are no variables that needs catching, there is no closure. The local variables i and ch and the parameter input are local to the scope, so each call has their own set of those variables. Any other variables are global, so they are shared between all calls.
The variable usedChars is changed in the code, and that change is visible to the code when the function does the recursive call, and then the variable is changed back to the previous state for the next iteration. When the function exists, the variable has the value that it had when the function was entered.
"does the function return permArr for each time it's called?"
Yes, but when the function calls itself, the return value is ignored. It's only when the array is returned from the outermost call that it is used.

Related

Recursion and Arrays. What does 'return' actually do here?

I'm currently working through freeCodeCamp's JS course.
One of the last problems asks you to create a recursive function that only accepts one argument n and creates an array that counts down from n to 1.
I was able to solve the problem using this code (SPOILERS IF YOU ARE ALSO WORKING ON THIS PROBLEM):
// Only change code below this line
function countdown(n) {
if (n < 1) {
return [];
} else {
const countArray = countdown(n - 1);
countArray.unshift(n);
return countArray;
}
}
// Only change code above this line
// my test
console.log(countdown(1))
I mostly arrived at this answer by copying syntax in the provided example. I plugged my answer into Python Tutor's code visualizer here. I will be referencing the steps in this visualizer.
Question about step 3: I notice it says countArray (block 1) is undefined. I assume this is because the function is hanging onto n and will go back and populate the array once the base statement creates it? Does this mean the defining of the array is delayed until the base case is reached?
Question on step 6: I see that my code worked as intended and now that n is 0, the base case is activated and the function returns an empty array. How does the code know that I want to populate this empty array with countArray? What ties the two together.
Question on step 7: If you can only answer one of my questions, I would like it to be this one.: Why does the function continue at all after the base case was reached (when n = 0)? From my flawed understanding return ends the function immediately. By this logic, my code shouldn't do what is intended. It would always count n down, and then regardless return an empty array.
Thank you for reading my question. If my thoughts are not detailed clearly enough here, please let me know how I can clarify.
Recursive function calls are stacked on top of the previous call.
So taking an example of countdown(2),
n > 0, i.e run else part which saves the value returned by countdown(1).
So now we go to countdown(1), which sends us to countdown(0).
Now, countdown(0) returns us an empty array.
From here, we run the left over part of countdown(1), i.e countArray = [] , which we got from countdown(0)
unshift means push value at start of array. So now our array is countArray=[1]
We return this value to countdown(2) (the first line). And now our countArray = [1] is in our initial function call.
Then we run the left over part again: countArray.unshift(2) -> [2, 1]
And finally we return it to whatever called it.
// Call Stack:
countdown(2) -> countdown(1) -> countdown(0)
// return Stack:
[2, 1] <- [1] <- []
Bonus: below is a one liner for the required function
const countdown = (n) => n ? [n, ...countdown(n - 1)] : []
console.log(countdown(5))
No array is created until you reach the base case.
When you call a function, a new context with its own local variables (called a stack frame) is created. Your visualizer shows these on the right ("Frames").
Notice how a new frame appears going from step 3 to 4. Then, when the base case returns going from step 6 to 7, the frame disappears again. The execution picks up where it left off before evaluating the base case, with the only difference being that the value for countArray is known.
return only finishes the current context, returning the intermediate result to its parent context. Basically, each level of recursion is independent here.

Confused on reversing an array

I want to reverse an array that I input into a function.
But for some reason the console returns the first value of the array instead of taking the full array within the while loop so it can unshift the value at the end to the front then delete that same value.
function littleWHEW(lemonade) {
let i = lemonade.length - 1;
while (i >= 0) {
lemonade.unshift(lemonade[i])
lemonade.pop(lemonade[i])
i = i - 1
}
return lemonade
}
console.info(littleWHEW([1,2,3,4,5]))
Just use the
reverse()
method:
function littleWHEW(lemonade) {
return lemonade.reverse();
}
You should not add a parameter to the pop() method, by the way.
Since you asked for explanation, let's take a deep investigation. Before we do, please take a look at
unshift and pop
Firstly i = 4. Here're what happens in one loop:
[1,2,3,4,5] is the original array. lemonade.unshift(lemonade[i]) adds one element to the first position of lemonade. At this point i = 4 so lemonade[4] = 5, we have [5,1,2,3,4,5] (notice the bold).
Now you pop the last one out. [5,1,2,3,4,5] becomes [5,1,2,3,4].
You decrease i.
Now i = 3, And here're what happens in second loop:
[5,1,2,3,4] is the original array. lemonade.unshift(lemonade[i]) adds one element to the first position of lemonade. At this point i = 3 so lemonade[3] = 3, we have [3,5,1,2,3,4] (notice the bold).
Now you pop the last one out. [3,5,1,2,3,4] becomes [3,5,1,2,3].
You decrease i.
After one loop, your i does not point to the last element as expected, and makes things wrong (as second loop does).

Flattening array understanding code

In another question of mine someone posted a really cool solution on how to flatten an nth nested array into one array. Since I did not want to start a long chat, and I still don't really fully understand what this code does, I thought I'd ask.
So my impression is that first in this case our array has length 2, and then it becomes 1 in the while loop. We then check is array[1], is an array. It is so we proceed. Now here is where I'm a bit confused. I believe we call the flatten function again so we can get into the nested arrays, but I'm still kind of hazy on the reasoning. We then take array[1] and slice it, here doesn't slicing just mean getting the whole array[l] anyways? since we go from 0th position to the end since slice() has no parameters.
function flatten(array) {
var l = array.length, temp;
while (l--) {
if (Array.isArray(array[l])) {
flatten(array[l]);
temp = array[l].slice();
temp.unshift(1);
temp.unshift(l);
[].splice.apply(array, temp);
}
}
}
var array = [['1', '2', '3'], ['4', '5', ['6'], ['7', '8']]];
flatten(array);
console.log(array);
https://jsfiddle.net/curw7mdp/
So I'm going to assume you understand the basics of recursion. I'll walk you through line by line.
var l = array.length, temp;
Declares l equal to the length of the array, and declares temp.
while (l--)
This decrements l after the iteration of the loop (as opposed to --l doing it before);
if (Array.isArray(array[l]))
This is checking if the 'l'th element in the array is another array. This is important because it means this element isn't flat.
flatten(array[l]);
This is where it gets fun, the function recursively calls itself so that it can now traverse the sub-array. And if the sub-array contains another array, it can keep going down deeper. I believe this is head recursion.
temp = array[l].slice();
Looks a little weird, but this allows us to extract the array into a new variable called temp.
temp.unshift(1);
temp.unshift(l);
[].splice.apply(array, temp);
This is also a very janky way of writing things, but basically it puts 1 and l as the first to elements in the temp array, and then it calls splice on array, with temp as the parameters. Only the first two elements of temp are passed as parameters (the two we put in just a second ago), so it uses basically removes the sub array, and replaces it with the flattened version.
References: splice(), apply(), unshift()
See comments for detailed explanation.
function flatten(array) {
// put the length of the array in l
// and create a temp variable with nothing in it
var l = array.length, temp;
// while(l--) will iterate through
// each item in the array backwards
// this is just a little faster than doing
// for(var i=0; i<array.length; i++)
while (l--) {
// The current item in the while loop is array[l]
// (that's an L, by the way, not a one)
// so this is saying "if current item is an array...."
if (Array.isArray(array[l])) {
// we call the function again (recursion)
// if this is an array. eventually, one of
// the inner items will be something other
// than an array and the recursion will stop
flatten(array[l]);
// when you call slice() with no parameters
// all it does is create a copy of the entire array
// and then we store it in temp
temp = array[l].slice();
// adds the number 1 to the begining of the array
// after we unshift one more time below,
// this will become the second parameter for the
// splice() call below, which is the number of
// items to delete
temp.unshift(1);
// adds the current index to the begining of the array
// this will be the first argument passed to splice()
// below, the first argument for splice is the
// index to start splicing..
temp.unshift(l);
// apply()'s first argument is context, in this case
// passing "array" as the context means the action will
// be performed on the array variable,
// which is the original arrray..
// apply()'s second argumetn is an array, each item
// of the array is passed in order to the function
// as arguments
// So basically this is saying.. "remove the current
// index (which is an array) and replace it with each
// of the items that were in that array
[].splice.apply(array, temp);
}
}
}

Javascript: Passing Address of Array Element into Function

In FORTRAN and C++, the address of a specific array element can be passed into a function. For example, in the main routine, WA1 is a work array of size 25 and offset is an integer variable that indicates the offset from the 0-index. Say offset is presently 6.
The declaration of the sub-routine might look like the following:
void Array_Manip1(double* WorkArray1){
. . .
When the sub-routine is called in the main program, the call might look like this:
Array_Manip1(&WA1[offset]);
By doing this, I can index WorkArray1 within the sub-routine starting at the 0-index, but knowing it is actually WA1[6].
e.g. -
for (int i = 0; i < 19; ++i)
WorkArray1[i] = whatever computation is required.
To do this in Javascript, I suppose the full array could be passed in to the sub-routine, plus one more variable to hold the offset. And then within the sub-routine, the offset would have to be added to the array index value.
e. g. -
for (int i = 0; i < 19; ++i){
WorkArray1[offset + i] = whatever computation is required.
But now I am passing one more variable into the sub-routine, and have to add the offset to the array index each time through the loop.
Is there a better way to do this in Javascript?
Is there a way to imitate C++'s ability to pass the address of a specific array element into a function?
The cleanest way would be to splice the array and pass in a subarray from the current index on. That way you still have one reference, and everything stays clean.
But no, arrays in most higher level languages do not allow you to reference a single element and then get back to the array. It is dangerous for a number of reasons on those kinds of languages where the underlying data may not even be stored contiguously. JavaScript is no exception, and you can pass in an array and an index, or a subarray, but you can't pass in a reference to an element in the array and get back to the array after passing it in.
Tim's answer is correct. I just want to add something about the C-like typed arrays: they can be created as a view into an ArrayBuffer, in which case you could create a new view of the same buffer as the larger array but starting at an offset, and pass that, without duplicating the underlying data. Closest you can get to your pointers.
You can sort of do what you want. And in fact, sometimes javascript can only do what you want. It all depends on what data the array contains.
In Javascript, the content of a variable may either be a value or a reference (pointer but without pointer arithmetic). But you have no choice in the matter. Numbers and strings are always values (there are exceptions but none of them apply when passing as function arguments) and everything else are always references.
So to get the behavior you want, simply use an object or array as your value holder instead of a string or number:
var ref_array = [ {value:1}, {value:2}, {value:3} ];
function increment (v_obj) {
v_obj.value ++;
}
var ref = ref_array[1];
increment(ref);
// ref_array will now contain: [{value:1},{value:3},{value:3}]
It's not that simple though. While the object appears to be passed by reference, the reference is however copied when the function is called. What this means is that ref and ref_array[1] and v_obj are three separate variables that point to the same thing.
For example, this wouldn't work:
function replace (obj1, obj2) {
obj1 = obj2;
}
replace(ref_array[1], {value:9});
// ref_array is still: [{value:1},{value:3},{value:3}]
That's because, while obj1 in the function above points to the same object as ref_array[1], it is not really a pointer to ref_array[1] but a separate variable. In C, this would be something like obj1 = &ref_array[1]. So passing an argument passes a copy of the pointer but not the pointer itself.

filling a multidimensional array within "for" loop (javascript)

It seems like JavaScript somehow tries to optimize code, so if we want to fill a multidimensional array (largeArr) with changing values of one-dimensional array (smallArr) within a loop and use this code:
largeArr = []
smallArr = []
for (i=0; i<2; i++)
{
smallArr[0]=i
smallArr[1]=2*i
largeArr[i]=smallArr
}
we get an unexpected result: largeArr=[[1,2],[1,2]] (must be [[0,0],[1,2]]). So, Javascript calculates smallArr values in the first place, and only then fills largeArr.
To get the right result we must declare smallArr in the loop:
largeArr = []
for (i=0; i<2; i++)
{
smallArr = []
smallArr[0]=i
smallArr[1]=2*i
largeArr[i]=smallArr
}
and then it works as expected (largeArr=[[0,0],[1,2]]).
Why does it behave this way?
Because Pointers, that's why. Javascript takes after Java, and C, in this (and only this) way. When you do the assignment
largeArr[i] = smallArr
you're assigning a pointer. A breakdown of pointers:
In C, (and to a lesser extent, Java and Javascript) you don't have a basic array type - instead, an array points to a space in memory, and you can fill that space with whatever information you want (or rather, you've declared). The way a pointer exists in memory? A four (or eight, or two, depending on your system) byte memory address, which tells the compiler/parser where to get the appropriate in formation. So, when you do that assignment there, you're telling it: "Hey, set largeArr[i] equal to the memory address of smallArr." Thus, when you make changes to smallArr, it's reflected every time you dereference the array - because it's actually the same array. But when you do:
smallArr = []
inside the loop, you're saying, "make a new array, and set smallArr equal to the address of that array." That way, the arrays stay separate.
With the line largeArr[i]=smallArr, you set the i property to a reference to the smallArr. You do not copy it. In the end, all properties of the largeArr will point to the same one smallArr, where you have overwritten the values each time.
By initializing the smallArr each loop turn, you create new objects; so each property of largeArr will point to a different array. Btw, it is an assignment, not a declaration - you would (and should) declare the variables as local (to the function) with a var statement.
In the last for iteration
smallArr[0]=i
smallArr[1]=2*i
(where i=1) the above code is transformed into :
smallArr[0]=1
smallArr[1]=2
And your big array is nothing than this :
[smallArr, smallArr]
which leads to the unexpected result :
[[1, 2], [1, 2]]
In javascript objects are copyed by reference (a kind of c style pointer).
In order to have the desired result, you must copy the array by value, or assign a different array in each loop :
var largeArr = [];
for (i=0; i<2; i++)
largeArr[i] = [[i, 2*i]];
When you assign an array reference as you have above, you're not assigning the values of that array, but just a reference to the array.
Think of it as a pointer. largeArr[0] and largeArr[1] are pointing to smallArr, and the loop iterations are simply changing the contents of smallArr. The thing to which largeArr is being "pointed" is not changing.

Categories

Resources