Skip to content

Update while-for #725

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 11, 2019
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 45 additions & 47 deletions 1-js/02-first-steps/12-while-for/article.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Loops: while and for

We often have a need to perform similar actions many times in a row.
We often need to repeat actions.

For example, when we need to output goods from a list one after another. Or just run the same code for each number from 1 to 10.
For example, outputting goods from a list one after another or just running the same code for each number from 1 to 10.

*Loops* are a way to repeat the same part of code multiple times.
*Loops* are a way to repeat the same code multiple times.

## The "while" loop

Expand All @@ -31,11 +31,11 @@ while (i < 3) { // shows 0, then 1, then 2

A single execution of the loop body is called *an iteration*. The loop in the example above makes three iterations.

If there were no `i++` in the example above, the loop would repeat (in theory) forever. In practice, the browser provides ways to stop such loops, and for server-side JavaScript we can kill the process.
If `i++` was missing from the example above, the loop would repeat (in theory) forever. In practice, the browser provides ways to stop such loops, and in server-side JavaScript, we can kill the process.

Any expression or a variable can be a loop condition, not just a comparison. They are evaluated and converted to a boolean by `while`.
Any expression or variable can be a loop condition, not just comparisons: the condition is evaluated and converted to a boolean by `while`.

For instance, the shorter way to write `while (i != 0)` could be `while (i)`:
For instance, a shorter way to write `while (i != 0)` is `while (i)`:

```js run
let i = 3;
Expand Down Expand Up @@ -68,7 +68,7 @@ do {
} while (condition);
```

The loop will first execute the body, then check the condition and, while it's truthy, execute it again and again.
The loop will first execute the body, then check the condition, and, while it's truthy, execute it again and again.

For example:

Expand All @@ -80,11 +80,11 @@ do {
} while (i < 3);
```

This form of syntax is rarely used except when you want the body of the loop to execute **at least once** regardless of the condition being truthy. Usually, the other form is preferred: `while(…) {…}`.
This form of syntax should only be used when you want the body of the loop to execute **at least once** regardless of the condition being truthy. Usually, the other form is preferred: `while(…) {…}`.

## The "for" loop

The `for` loop is the most often used one.
The `for` loop is the most commonly used loop.

It looks like this:

Expand All @@ -102,14 +102,14 @@ for (let i = 0; i < 3; i++) { // shows 0, then 1, then 2
}
```

Let's examine the `for` statement part by part:
Let's examine the `for` statement part-by-part:

| part | | |
|-------|----------|----------------------------------------------------------------------------|
| begin | `i = 0` | Executes once upon entering the loop. |
| condition | `i < 3`| Checked before every loop iteration, if fails the loop stops. |
| step| `i++` | Executes after the body on each iteration, but before the condition check. |
| body | `alert(i)`| Runs again and again while the condition is truthy |
| condition | `i < 3`| Checked before every loop iteration. If false, the loop stops. |
| step| `i++` | Executes after the body on each iteration but before the condition check. |
| body | `alert(i)`| Runs again and again while the condition is truthy. |


The general loop algorithm works like this:
Expand All @@ -121,9 +121,9 @@ Run begin
→ ...
```

If you are new to loops, then maybe it would help if you go back to the example and reproduce how it runs step-by-step on a piece of paper.
If you are new to loops, it could help to go back to the example and reproduce how it runs step-by-step on a piece of paper.

Here's what exactly happens in our case:
Here's exactly what happens in our case:

```js
// for (let i = 0; i < 3; i++) alert(i)
Expand All @@ -140,7 +140,7 @@ if (i < 3) { alert(i); i++ }
```

````smart header="Inline variable declaration"
Here the "counter" variable `i` is declared right in the loop. That's called an "inline" variable declaration. Such variables are visible only inside the loop.
Here, the "counter" variable `i` is declared right in the loop. This is called an "inline" variable declaration. Such variables are visible only inside the loop.

```js run
for (*!*let*/!* i = 0; i < 3; i++) {
Expand All @@ -149,7 +149,7 @@ for (*!*let*/!* i = 0; i < 3; i++) {
alert(i); // error, no such variable
```

Instead of defining a variable, we can use an existing one:
Instead of defining a variable, we could use an existing one:

```js run
let i = 0;
Expand Down Expand Up @@ -190,25 +190,25 @@ for (; i < 3;) {
}
```

The loop became identical to `while (i < 3)`.
This makes the loop identical to `while (i < 3)`.

We can actually remove everything, thus creating an infinite loop:
We can actually remove everything, creating an infinite loop:

```js
for (;;) {
// repeats without limits
}
```

Please note that the two `for` semicolons `;` must be present, otherwise it would be a syntax error.
Please note that the two `for` semicolons `;` must be present. Otherwise, there would be a syntax error.

## Breaking the loop

Normally the loop exits when the condition becomes falsy.
Normally, a loop exits when its condition becomes falsy.

But we can force the exit at any moment. There's a special `break` directive for that.
But we can force the exit at any time using the special `break` directive.

For example, the loop below asks the user for a series of numbers, but "breaks" when no number is entered:
For example, the loop below asks the user for a series of numbers, "breaking" when no number is entered:

```js
let sum = 0;
Expand All @@ -227,15 +227,15 @@ while (true) {
alert( 'Sum: ' + sum );
```

The `break` directive is activated at the line `(*)` if the user enters an empty line or cancels the input. It stops the loop immediately, passing the control to the first line after the loop. Namely, `alert`.
The `break` directive is activated at the line `(*)` if the user enters an empty line or cancels the input. It stops the loop immediately, passing control to the first line after the loop. Namely, `alert`.

The combination "infinite loop + `break` as needed" is great for situations when the condition must be checked not in the beginning/end of the loop, but in the middle, or even in several places of the body.
The combination "infinite loop + `break` as needed" is great for situations when a loop's condition must be checked not in the beginning or end of the loop, but in the middle or even in several places of its body.

## Continue to the next iteration [#continue]

The `continue` directive is a "lighter version" of `break`. It doesn't stop the whole loop. Instead it stops the current iteration and forces the loop to start a new one (if the condition allows).
The `continue` directive is a "lighter version" of `break`. It doesn't stop the whole loop. Instead, it stops the current iteration and forces the loop to start a new one (if the condition allows).

We can use it if we're done on the current iteration and would like to move on to the next.
We can use it if we're done with the current iteration and would like to move on to the next one.

The loop below uses `continue` to output only odd values:

Expand All @@ -249,9 +249,9 @@ for (let i = 0; i < 10; i++) {
}
```

For even values of `i` the `continue` directive stops body execution, passing the control to the next iteration of `for` (with the next number). So the `alert` is only called for odd values.
For even values of `i`, the `continue` directive stops executing the body and passes control to the next iteration of `for` (with the next number). So the `alert` is only called for odd values.

````smart header="The directive `continue` helps to decrease nesting level"
````smart header="The `continue` directive helps decrease nesting"
A loop that shows odd values could look like this:

```js
Expand All @@ -264,13 +264,13 @@ for (let i = 0; i < 10; i++) {
}
```

From a technical point of view it's identical to the example above. Surely, we can just wrap the code in the `if` block instead of `continue`.
From a technical point of view, this is identical to the example above. Surely, we can just wrap the code in an `if` block instead of using `continue`.

But as a side-effect we got one more nesting level (the `alert` call inside the curly braces). If the code inside `if` is longer than a few lines, that may decrease the overall readability.
But as a side-effect, this created one more level of nesting (the `alert` call inside the curly braces). If the code inside of`if` is longer than a few lines, that may decrease the overall readability.
````

````warn header="No `break/continue` to the right side of '?'"
Please note that syntax constructs that are not expressions cannot be used with the ternary operator `?`. In particular, directives such as `break/continue` are disallowed there.
Please note that syntax constructs that are not expressions cannot be used with the ternary operator `?`. In particular, directives such as `break/continue` aren't allowed there.

For example, if we take this code:

Expand All @@ -282,24 +282,24 @@ if (i > 5) {
}
```

...And rewrite it using a question mark:
...and rewrite it using a question mark:


```js no-beautify
(i > 5) ? alert(i) : *!*continue*/!*; // continue not allowed here
(i > 5) ? alert(i) : *!*continue*/!*; // continue isn't allowed here
```

...Then it stops working. The code like this will give a syntax error:
...it stops working. Code like this will give a syntax error:


That's just another reason not to use a question mark operator `?` instead of `if`.
This is just another reason not to use the question mark operator `?` instead of `if`.
````

## Labels for break/continue

Sometimes we need to break out from multiple nested loops at once.

For example, in the code below we loop over `i` and `j` prompting for coordinates `(i, j)` from `(0,0)` to `(3,3)`:
For example, in the code below we loop over `i` and `j`, prompting for the coordinates `(i, j)` from `(0,0)` to `(3,3)`:

```js run no-beautify
for (let i = 0; i < 3; i++) {
Expand All @@ -318,7 +318,7 @@ alert('Done!');

We need a way to stop the process if the user cancels the input.

The ordinary `break` after `input` would only break the inner loop. That's not sufficient. Labels come to the rescue.
The ordinary `break` after `input` would only break the inner loop. That's not sufficient--labels, come to the rescue!

A *label* is an identifier with a colon before a loop:
```js
Expand All @@ -327,9 +327,7 @@ labelName: for (...) {
}
```

The `break <labelName>` statement in the loop breaks out to the label.

Like here:
The `break <labelName>` statement in the loop below breaks out to the label:

```js run no-beautify
*!*outer:*/!* for (let i = 0; i < 3; i++) {
Expand All @@ -347,7 +345,7 @@ Like here:
alert('Done!');
```

In the code above `break outer` looks upwards for the label named `outer` and breaks out of that loop.
In the code above, `break outer` looks upwards for the label named `outer` and breaks out of that loop.

So the control goes straight from `(*)` to `alert('Done!')`.

Expand All @@ -358,10 +356,10 @@ outer:
for (let i = 0; i < 3; i++) { ... }
```

The `continue` directive can also be used with a label. In this case the execution jumps to the next iteration of the labeled loop.
The `continue` directive can also be used with a label. In this case, code execution jumps to the next iteration of the labeled loop.

````warn header="Labels are not a \"goto\""
Labels do not allow us to jump into an arbitrary place of code.
Labels do not allow us to jump into an arbitrary place in the code.

For example, it is impossible to do this:
```js
Expand All @@ -370,7 +368,7 @@ break label; // jumps to label? No.
label: for (...)
```

The call to a `break/continue` is only possible from inside the loop, and the label must be somewhere upwards from the directive.
A call to `break/continue` is only possible from inside a loop and the label must be somewhere above the directive.
````

## Summary
Expand All @@ -383,6 +381,6 @@ We covered 3 types of loops:

To make an "infinite" loop, usually the `while(true)` construct is used. Such a loop, just like any other, can be stopped with the `break` directive.

If we don't want to do anything on the current iteration and would like to forward to the next one, the `continue` directive does it.
If we don't want to do anything in the current iteration and would like to forward to the next one, we can use the `continue` directive.

`break/continue` support labels before the loop. A label is the only way for `break/continue` to escape the nesting and go to the outer loop.
`break/continue` support labels before the loop. A label is the only way for `break/continue` to escape a nested loop to go to an outer one.