Hexagon Theory
Hexagon Theory

Reputation: 44813

Regarding JavaScript for() loop voodoo

I was for quite some time under the impression that a for loop could exist solely in the following format:

for (INITIALIZER; STOP CONDITION; INC(DEC)REMENTER)
 {
    CODE
 }

This is, however, most definitely not the case; take a look at this JavaScript implementation of the Fisher-Yates Shuffle:

shuffle = function(o)
 {
    for (var j, x, i = o.length; i; j = parseInt(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
    return o;
 };

This little snippet completely blows my mind; how in the world is so much going on inside a simple for loop declaration? I mean... it doesn't even open a brace! All of the magic is being done right there inside the for statement. It'd be absolutely wonderful if somebody could provide a relatively thorough explanation as to how in the world this voodoo is doing what it does. Much appreciated in advance.

Upvotes: 13

Views: 3815

Answers (9)

Scott Evernden
Scott Evernden

Reputation: 39926

this goes all the way back to C syntax - from which javascript has stole a bunch. the main trick is the comma-operator which seems to appear in almost no other place except for loops

Upvotes: 1

Yuliy
Yuliy

Reputation: 17718

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 ";"

Upvotes: 8

Matthew Crumley
Matthew Crumley

Reputation: 102735

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;
};

Upvotes: 11

Peter Richards
Peter Richards

Reputation: 629

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

Upvotes: 1

slim
slim

Reputation: 41223

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).

Upvotes: 7

OscarRyz
OscarRyz

Reputation: 199234

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.

Upvotes: 1

Phil H
Phil H

Reputation: 20141

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.

Upvotes: 14

Milan Babuškov
Milan Babuškov

Reputation: 61148

Syntax of for loop is:

for (pre-block; condition; post-loop-block)
    loop-block;

First, pre-block is executed, various variables are defined.

In each loop:

  1. check condition
  2. execute loop-block
  3. execute post-loop-block
  4. repeat from 1.

Upvotes: 4

Misko
Misko

Reputation: 2044

The first clause initializes any variables you want to use. The second clause is indeed the stop condition. The third clause includes any logic to be executed at the end of each iteration. Multiple statements can be separated by commas.

Upvotes: 0

Related Questions