5

So I was reading about shuffling an array. And then I came across this script:

shuffle = function(o){ //v1.0
    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;
};

When I look closely, the for does not even have any {} at all! But it is working, like magic. I am very curious to know how it work. (and the bunch of commas too.)

Community
  • 1
  • 1
Derek 朕會功夫
  • 92,235
  • 44
  • 185
  • 247
  • If there is only one for loop then the script will work, but if you have more than one right after the other then you have to separate the statements. – Rayshawn Jun 13 '12 at 04:02

6 Answers6

13

What follows the for () can be any statement; that can be something with curly braces, or it can be a single expression, or it can be an empty expression. for (...); is equivalent to for (...) {}. Naturally, this should only be used in conjunction with a for-loop which will terminate naturally, or you'll have an infinite loop on your hands.

The commas are effectively second-grade semicolons; they make for mostly-separate statements, but which will work in a for loop (and elsewhere; this is a very sloppy definition of them).

for (
     // initialisation: declare three variables
     var j, x, i = o.length;
     // The loop check: when it gets to ``!i``, it will exit the loop
     i;
     // the increment clause, made of several "sub-statements"
     j = parseInt(Math.random() * i),
     x = o[--i],
     o[i] = o[j],
     o[j] = x
)
    ; // The body of the loop is an empty statement

This could be put in a more readable form as:

for (
     // initialisation: declare three variables
     var j, x, i = o.length;
     // The loop check: when it gets to ``!i``, it will exit the loop
     i;
     // note the increment clause is empty
) {
     j = parseInt(Math.random() * i);
     x = o[--i];
     o[i] = o[j];
     o[j] = x;
}

As a while loop, that could be:

var j, x, i = o.length;
while (i) {
     j = parseInt(Math.random() * i);
     x = o[--i];
     o[i] = o[j];
     o[j] = x;
}
Chris Morgan
  • 86,207
  • 24
  • 208
  • 215
4

Every computing is in same statement for single statement we dont need {} but also in this statement ; (sentence terminator is used in the end) it means next statement does not belong to for statement. Whatever logic is it is in same for statement.

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


  for(var j, x, i = o.length;// Initialization
  i;// Work until i is zero
 j = parseInt(Math.random() * i),
  x = o[--i], o[i] = o[j], o[j] = x);//Here he is    computing his logic
Amritpal Singh
  • 1,765
  • 1
  • 13
  • 20
4

First, I'd like to note that such a loop is considered bad style, since it's very unreadable and causes a lot of confusion. This is a typical example of optimization gone wrong.

Looking at the specs, you'll find that for(...) must be followed by a statement. It can be any statement, including blocks. So all of these are valid:

for (...)
    foo;  // expression statement

,

for(...)
    {
       // block statement
    }

,

for(...)
   if(...) // If statement
      foo;

, and of course

for (...)
    ;

since ";" is the empty statement. It does nothing, but it's enough to make for(...); syntactically valid.

Now, for the commas. Note that the contents of the parens must be three expressions, (each of them optional), separated by semicolons. Pretty much "everything" qualifies as expressions, including comma-separated lists of expressions. Though little known, these work basically everywhere in JS, not only in for loops. They are simply evaluated one after the other.

So, your loop can be rewritten like so

shuffle = function(o) {
    var j, x, i = o.length;
    while (i) { // for-loops are just while-loops in disguise
        j = parseInt(Math.random() * i), // even better: replace , by ;
        x = o[--i],
        o[i] = o[j],
        o[j] = x;
    }
    return o;
};

Also, x = o[--i] should be written as i--; x = o[i].

user123444555621
  • 148,182
  • 27
  • 114
  • 126
  • `must be followed by a statement...` Not really, in the specs: `b. Let "stmt" be the result of evaluating "Statement". c. If stmt.value is not empty,...` It has to check if there is something, because "Statement" can be empty. – Derek 朕會功夫 Jun 13 '12 at 04:55
  • "It has no check if there is something" - well, it says *Statement* in italics, so there has to be a [statement](http://es5.github.com/#x12), which can not be "nothing". Its *value* can be empty, take for example the `return;` statement. – user123444555621 Jun 13 '12 at 04:59
  • This is confusing. So *nothing* is interpreted as *nothing* or an *EmptyStatement*? – Derek 朕會功夫 Jun 13 '12 at 05:01
  • *nothing* is not interpreted at all. The parser will continue searching for a valid statement. That's exactly why the `;` (EmptyStatement) is required. Otherwise part of the next line may be "eaten up", or you might get a syntax error. – user123444555621 Jun 13 '12 at 05:04
1

If it's all on one line, no brackets are necessary. A lot of times in that third section inside the parenthesis, you just see i++, or something like that. Really, you can do many different things there. If you are able to pack everything in that third section, you don't even need a body for the for loop.

for (first section; second section; third section);

First section

Variables are declared and initialized. These variables are contained by the scope of the loop.

Second section

This is the condition checked with each pass of the loop.

Third section

Code that runs after each pass through the loop. It can be as simple as incrementing a variable, and as complex as... well, anything you can fit on the line, as long as syntax is correct.

woz
  • 10,888
  • 3
  • 34
  • 64
  • What about using `for` with `in`, like here: `for (var property in css) element.style[property] = css[property];` – can we insert the statement inside somehow? – Ωmega Feb 13 '23 at 15:19
1

Looks like I didn't read the loop correctly.

In this case, the evaluations are happening inside the conditions of the for loop.

So a for loop has three parts

for (initial variables; end cases; what to do every iteration)

You define some initial stuff and use o which was passed into the function, define an end case, and then compute something every iteration. At the end, o has a new value and it gets returned.

Derek 朕會功夫
  • 92,235
  • 44
  • 185
  • 247
sachleen
  • 30,730
  • 8
  • 78
  • 73
0

All the work is actually being done within the parens of the for statement. There is not explicit body for the loop so the ; at the end just says that the body is an empty statement.

The , (comma) operator evaluates expressions from left to right, and returns the value of the right-most.

The loop is basically equivalent to:

for(var j, x, i = o.length; i > 0;)
{
    j = parseInt(Math.random() * i--);
    x = o[i];
    o[i] = o[j];
    o[j] = x
} 
Andrew Cooper
  • 32,176
  • 5
  • 81
  • 116
  • When javascript evaluates the loop condition (`i`) it evaluates to true for any non-zero value, and false when the value is 0. Saying `i > 0` is just a more explicit way of saying the same thing. The loop is counting down from the length of the array to 0. – Andrew Cooper Jun 13 '12 at 04:27