I was for quite some time under the impression that a for
loop could exist solely in the following format:
for (INITIALIZER; STOP CONDITION
The generalized format of a for loop (not a for-in loop) is
for ( EXPRESSION_1 ; EXPRESSION_2 ; EXPRESSION_3 ) STATEMENT
The first EXPRESSION_1 is usually used to initialize the loop variable, EXPRESSION_2 is the looping condition, and EXPRESSION_3 is usually an increment or decrement operation, but there are no rules that say they have to behave like that. It's equivalent to the following while loop:
EXPRESSION_1;
while (EXPRESSION_2) {
STATEMENT
EXPRESSION_3;
}
The commas are just an operator that combines two expressions into a single expression, whose value is the second sub-expression. They are used in the for loop because each part (separated by semicolons) needs to be a single expression, not multiple statements. There's really no reason (except maybe to save some space in the file) to write a for loop like that since this is equivalent:
shuffle = function(o) {
var j, x;
for (var i = o.length; i > 0; i--) {
j = parseInt(Math.random() * i);
x = o[i - 1];
o[i - 1] = o[j];
o[j] = x;
}
return o;
};
That statement does comply with your initial format.
It turns out you could add more than one sentence of each using "," ( comma )
So:
for (var j, x, i = o.length; i; j = parseInt(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
Could be analyzed like this:
for (var j, //INITIALIZER(s)
x,
i = o.length;
i; // STOP CONDITION ( i )
j = parseInt(Math.random() * i), // INC(DEC)REMENTER
x = o[--i],
o[i] = o[j],
o[j] = x); // CODE ( ; )
As you see, it fits completely in your initial format.
They've pretty much just moved the body of the loop into the incrementer section. You can re-write the for loop as a while loop to get some idea of what it is doing:
shuffle=function(o) {
var j; //Random position from 0 up to the current position - 1
var x; //temp holder for swapping positions
var i=o.length; //current position
while(i>0) { // Loop through the array
j = parseInt(Math.random()*i); //get a lower position
x = o[--i]; // decrement the position and store that position's value in the temp var
o[i]=o[j]; // copy position j to position i
o[j]=x; // copy the temp value that stored the old value at position i into position j
}
return o;
}
The first three var's are the initialzier expanded out, the check in the while is the stop condition and the body of the while is what was done in the incrementer portion of the for.
Edit: Corrected per Gumbo's comment
INITIALIZER can declare and initialize multiple variables. STOP CONDITION is a single test (here it's just "i"), and INCREMENTER is an expression to be executed each time after body (the comma operator lets you have multiple sub-expressions, which all get executed. ). The body of the for loop is just the empty statement ";"
shuffle = function(o){
for (
var j, // declare j
x, // declare x
i = o.length; // declare i and set to o.length
i; // loop while i evaluates true
j = parseInt(Math.random() * i), // j=random number up to i
x = o[--i], // decrement i, and look up this index of o
o[i] = o[j], // copy the jth value into the ith position
o[j] = x // complete the swap by putting the old o[i] into jth position
);
return o;
};
This is starting with i equal to the number of positions, and each time swapping the cards i and j, where j is some random number up to i each time, as per the algorithm.
It could be more simply written without the confusing comma-set, true.
By the way, this is not the only kind of for loop in javascript. There is also:
for(var key in arr) {
value = arr[key]);
}
But be careful because this will also loop through the properties of an object, including if you pass in an Array object.
The code you quote is obfuscated in my opinion. There are much clearer ways to write the same functionality.
However, your understanding is pretty much right. The following is the exact same code, except for whitespace and comments.
for (
// Initializer
var j, x, i = o.length;
// Continue condition
i;
// Operation to be carried out on each loop
j = parseInt(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x
)
// empty body, equivalent to { }
;
It's much clearer to write the equivalent:
var j,x,i = o.length;
while(i) {
j = parseInt(Math.random() * i);
x = o[--i];
o[i] = o[j];
o[j] = x;
}
There are other optimisations that could be made for readability - including using while(i > 0)
instead of while(i)
, and splitting out the --i
into an i--
on a separate line.
There's really no reason for for() to exist, except for readability. These two are equivalent:
{ // this block is to scope int i
int i=0;
while(i<100) {
myfunc(i);
i++;
}
}
for(int i=0; i<100; i++) {
myfunc(i);
}
You should use whichever is most readable for a given time. I'd argue that the author of your code has done the opposite. In fairness, he may have done this in order to achieve a smaller JS file for faster loading (this is the kind of transform an automated code compactor could do).