From 835d2ae1cebef7556efe28222b3f10895acdd6da Mon Sep 17 00:00:00 2001 From: Ilya Kantor Date: Sun, 30 Aug 2015 06:49:16 +0300 Subject: [PATCH] loop --- 1-js/2-first-steps/15-while-for/article.md | 286 ++++++++++++--------- 1 file changed, 161 insertions(+), 125 deletions(-) diff --git a/1-js/2-first-steps/15-while-for/article.md b/1-js/2-first-steps/15-while-for/article.md index 71991707..02830e48 100644 --- a/1-js/2-first-steps/15-while-for/article.md +++ b/1-js/2-first-steps/15-while-for/article.md @@ -1,24 +1,25 @@ -# Циклы while, for +# Loops: while and for -При написании скриптов зачастую встает задача сделать однотипное действие много раз. +We often have a need to perform similar actions many times in a row. -Например, вывести товары из списка один за другим. Или просто перебрать все числа от 1 до 10 и для каждого выполнить одинаковый код. +For example, when we need to output goods from the list one after another. Or just write the same code for each number from 1 to 10. + +*Loops* are a way to repeat the same part code multiple times. -Для многократного повторения одного участка кода - предусмотрены *циклы*. [cut] -## Цикл while +## The "while" loop -Цикл `while` имеет вид: +The `while` loop has the following syntax: ```js -while (условие) { - // код, тело цикла +while (condition) { + // code, loop body } ``` -Пока `условие` верно -- выполняется код из тела цикла. +While the `condition` is true -- the `code` from the loop body is executed. -Например, цикл ниже выводит `i` пока `i < 3`: +For instance, the loop below outputs `i` while `i<3`: ```js //+ run @@ -29,44 +30,49 @@ while (i < 3) { } ``` -**Повторение цикла по-научному называется *"итерация"*. Цикл в примере выше совершает три итерации.** +There's a special term *iteration* for each loop run. The loop in the example above makes 3 iterations. -Если бы `i++` в коде выше не было, то цикл выполнялся бы (в теории) вечно. На практике, браузер выведет сообщение о "зависшем" скрипте и посетитель его остановит. +If there were no `i++` in the example above, the loop would repeat (in theory) forever. In practice, the browser would show a message about a "hanging" script and let the user stop it. -**Бесконечный цикл** можно сделать и проще: - -```js -while (true) { - // ... -} -``` - -**Условие в скобках интерпретируется как логическое значение, поэтому вместо `while (i!=0)` обычно пишут `while (i)`**: +The `condition` is treated as a logical value, so instead of `while (i!=0)` we usually write `while (i)`: ```js //+ run var i = 3; *!* -while (i) { // при i, равном 0, значение в скобках будет false и цикл остановится +while (i) { // when i becomes 0, the condition is falsy and the loop stops */!* alert( i ); i--; } ``` -## Цикл do..while +[smart header="Brackes are not required for a single-line body"] -Проверку условия можно поставить *под* телом цикла, используя специальный синтаксис `do..while`: +If the loop body has a single statement, we can omit the brackets `{…}`: + +```js +//+ run +var i = 3; +*!* +while (i) alert(i--); +*/!* +``` +[/smart] + +## The "do..while" loop + +The condition check can be moved *below* the loop body using the `do..while` syntax: ```js do { - // тело цикла -} while (условие); + // loop body +} while (condition); ``` -Цикл, описанный, таким образом, сначала выполняет тело, а затем проверяет условие. +The loop will first execute the body and then check the condition. -Например: +For example: ```js //+ run @@ -77,52 +83,53 @@ do { } while (i < 3); ``` -Синтаксис `do..while` редко используется, т.к. обычный `while` нагляднее -- в нём не приходится искать глазами условие и ломать голову, почему оно проверяется именно в конце. +This form of syntax is rarely used, because the ordinary `while` is more obvious. We don't need to eye down the code looking for the condition. Neither we need to ask ourselves a question why it is checked at the bottom. +## The "for" loop -## Цикл for +The `for` loop is actually the most often used one. -Чаще всего применяется цикл `for`. Выглядит он так: +It looks like this: ```js -for (начало; условие; шаг) { - // ... тело цикла ... +for (begin; condition; step) { + // ... loop body ... } ``` -Пример цикла, который выполняет `alert(i)` для `i` от `0` до `2` включительно (до `3`): +An example of the loop which runs `alert(i)` for `i` from `0` to `3` not including `3`: ```js //+ run var i; -for (i = 0; i < 3; i++) { +for (i = 0; i < 3; i++) { // shows 0, then 1, then 2 alert( i ); } ``` -Здесь: +Here: -Цикл выполняется так: +The loop execution follows these steps:
    -
  1. Начало: `i=0` выполняется один-единственный раз, при заходе в цикл.
  2. -
  3. Условие: `i<3` проверяется перед каждой итерацией и при входе в цикл, если оно нарушено, то происходит выход.
  4. -
  5. Тело: `alert(i)`.
  6. -
  7. Шаг: `i++` выполняется после *тела* на каждой итерации, но перед проверкой условия.
  8. -
  9. Идти на шаг 2.
  10. +
  11. Begin: `i=0` executes only once upon entering the loop.
  12. +
  13. Condition: `i<3` is checked before every iteration including the first one. If it fails, the loop stops.
  14. +
  15. Body: `alert(i)` runs is the condition is truthy.
  16. +
  17. Step: `i++` executes after the `body` on each iteration, but before the `condition` check.
  18. +
  19. Continue to step 2.
-Иными словами, поток выполнения: `начало` -> (если `условие` -> `тело` -> `шаг`) -> (если `условие` -> `тело` -> `шаг`) -> ... и так далее, пока верно `условие`. +In other words, the execution flow is: `begin` -> (if `condition` -> run `body` and `step`) -> (if `condition` -> run `body` and `step`) -> ... and so on while the `condition` is truthy. -[smart] -В цикле также можно определить переменную: +[smart header="Inline variable declaration"] +We can declare a "counter" variable right in the beginning of the loop. ```js //+ run no-beautify @@ -130,26 +137,27 @@ for (*!*var*/!* i = 0; i < 3; i++) { alert(i); // 0, 1, 2 } ``` - -Эта переменная будет видна и за границами цикла, в частности, после окончания цикла `i` станет равно `3`. [/smart] -## Пропуск частей for -Любая часть `for` может быть пропущена. +## Skipping of "for" parts -Например, можно убрать `начало`. Цикл в примере ниже полностью идентичен приведённому выше: +Any part of the `for` can be skipped. + +For example, we can remove `begin`, or move it before the actual `for`, like here: ```js //+ run var i = 0; -for (; i < 3; i++) { +for (; i < 3; i++) { alert( i ); // 0, 1, 2 } ``` -Можно убрать и `шаг`: +It would work same as `for(var i=0; ...)`. + +We can also remove the `step` part: ```js //+ run @@ -157,39 +165,41 @@ var i = 0; for (; i < 3;) { alert( i ); - // цикл превратился в аналог while (i<3) + // the loop became identical to while (i<3) } ``` -А можно и вообще убрать всё, получив бесконечный цикл: +We can actually remove everything, thus creating an infinite loop: ```js for (;;) { - // будет выполняться вечно + // repeats without limits } ``` -При этом сами точки с запятой `;` обязательно должны присутствовать, иначе будет ошибка синтаксиса. +Although the semicolons `;` must present, otherwise it would be a syntax error. [smart header="`for..in`"] -Существует также специальная конструкция `for..in` для перебора свойств объекта. +There is also a special construct `for..in` to iterate over object properties. -Мы познакомимся с ней позже, когда будем [говорить об объектах](#for..in). +We'll get to it later while [talking about objects](#for..in). [/smart] -## Прерывание цикла: break +## Breaking the loop -Выйти из цикла можно не только при проверке условия но и, вообще, в любой момент. Эту возможность обеспечивает директива `break`. +Normally the loop exists when the condition becomes falsy. -Например, следующий код подсчитывает сумму вводимых чисел до тех пор, пока посетитель их вводит, а затем -- выдаёт: +But we can force the exit any moment. There's a special `break` directive for that. + +For example, this code calculates the sum of numbers until the user keeps entering them. And then outputs it: ```js var sum = 0; while (true) { - var value = +prompt("Введите число", ''); + var value = +prompt("Enter a number", ''); *!* if (!value) break; // (*) @@ -198,22 +208,20 @@ while (true) { sum += value; } -alert( 'Сумма: ' + sum ); +alert( 'Sum: ' + sum ); ``` -Директива `break` в строке `(*)`, если посетитель ничего не ввёл, полностью прекращает выполнение цикла и передаёт управление на строку за его телом, то есть на `alert`. +The `break` directive is activated in 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 it's loop. Namely, `alert`. -Вообще, сочетание "бесконечный цикл + break" -- отличная штука для тех ситуаций, когда условие, по которому нужно прерваться, находится не в начале-конце цикла, а посередине. +Actually, the composition: "an infinite loop + break" is a great thing for situations when the condition must be checked not in beginning/end of the loop, but in the middle. -## Следующая итерация: continue [#continue] +## Continue to the next iteration [#continue] -Директива `continue` прекращает выполнение *текущей итерации* цикла. +The `continue` is a younger sister of `break`. It doesn't break the whole loop, just the current body execution, as if it finished. -Она -- в некотором роде "младшая сестра" директивы `break`: прерывает не весь цикл, а только текущее выполнение его тела, как будто оно закончилось. +We can use it if we're done on the current iteration and would like to move to the next one. -Её используют, если понятно, что на текущем повторе цикла делать больше нечего. - -Например, цикл ниже использует `continue`, чтобы не выводить чётные значения: +The loop above uses `continue` to output only odd values: ```js //+ run no-beautify @@ -225,11 +233,10 @@ for (var i = 0; i < 10; i++) { } ``` -Для чётных `i` срабатывает `continue`, выполнение тела прекращается и управление передаётся на следующий проход `for`. +For even `i` the `continue` directive stops body execution, passing the control to the next iteration of `for` (with the next number). -[smart header="Директива `continue` позволяет обойтись без скобок"] - -Цикл, который обрабатывает только нечётные значения, мог бы выглядеть так: +[smart header="`continue` allows to decrease nesting level"] +A loop for odd-only values could look like this: ```js for (var i = 0; i < 10; i++) { @@ -241,51 +248,80 @@ for (var i = 0; i < 10; i++) { } ``` -С технической точки зрения он полностью идентичен. Действительно, вместо `continue` можно просто завернуть действия в блок `if`. Однако, мы получили дополнительный уровень вложенности фигурных скобок. Если код внутри `if` более длинный, то это ухудшает читаемость, в отличие от варианта с `continue`. +From the technical point of view it's identical. Really, we can just wrap the code in the `if` block instead of `continue`. + +But we got one more figure brackets nesting level. If the code inside `if` is longer than a few lines, that lowers the overall readability. [/smart] -[warn header="Нельзя использовать break/continue справа от оператора '?'"] -Обычно мы можем заменить `if` на оператор вопросительный знак `'?'`. +[warn header="No `break/continue` to the right side of '?'"] +Usually it is possible to replace `if` with a question mark operator `'?'`. -То есть, запись: +The code: ```js -if (условие) { +if (condition) { a(); } else { b(); } ``` -...Аналогична записи: +...Is identical to: ```js -условие ? a() : b(); +condition ? a() : b(); ``` -В обоих случаях в зависимости от условия выполняется либо `a()` либо `b()`. +In both cases either `a()` or `b()` is executed depending on the `condition`. -Но разница состоит в том, что оператор вопросительный знак `'?'`, использованный во второй записи, возвращает значение. +Please note that syntax constructs that do not return a value cannot be used in `'?'`. Directives `break/continue` are not expressions. They are disallowed here. -**Синтаксические конструкции, которые не возвращают значений, нельзя использовать в операторе `'?'`.** - -К таким относятся большинство конструкций и, в частности, `break/continue`. - -Поэтому такой код приведёт к ошибке: +The code below will give a syntax error: ```js //+ no-beautify (i > 5) ? alert(i) : *!*continue*/!*; ``` -Впрочем, как уже говорилось ранее, оператор вопросительный знак `'?'` не стоит использовать таким образом. Это -- всего лишь ещё одна причина, почему для проверки условия предпочтителен `if`. +That's one more reason not to use a question mark operator `'?'` instead of `if`. [/warn] -## Метки для break/continue +## Labels for break/continue -Бывает нужно выйти одновременно из нескольких уровней цикла. +Sometimes we need to break out from multiple nested loops at once. -Например, внутри цикла по `i` находится цикл по `j`, и при выполнении некоторого условия мы бы хотели выйти из обоих циклов сразу: +For example, in the code below we loop over `i` and `j` asking for values on coordinates `(i, j)` from `(0,0)` to `(3,3)`: + + +```js +//+ run no-beautify +for (var i = 0; i < 3; i++) { + + for (var j = 0; j < 3; j++) { + + var input = prompt(`Value at coords (${i},${j})`, ''); + // do something with the value... + + } +} + +alert('Done!'); +``` + +Let's say we need a way to stop the process. Like if we user decides to cancel the input. + +The ordinary `break` after `input` would only break the iterations over `j`. That's not sufficient. + +A *label* is an identifier with a colon before a loop: +```js +labelName: for(...) { + ... +} +``` + +We can put the `labelName` after a break statement, and it will break out of the labelled loop. + +Like here: ```js //+ run no-beautify @@ -293,27 +329,22 @@ if (условие) { for (var j = 0; j < 3; j++) { - var input = prompt('Значение в координатах '+i+','+j, ''); + var input = prompt(`Value at coords (${i},${j})`, ''); - // если отмена ввода или пустая строка - - // завершить оба цикла + // if an empty string or canceled, then break out of both loops if (!input) *!*break outer*/!*; // (*) + // do something with the value... } } -alert('Готово!'); +alert('Done!'); ``` -В коде выше для этого использована *метка*. +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!')`. -```js -//+ no-beautify -outer: for (var i = 0; i < 3; i++) { ... } -``` - -Можно также выносить её на отдельную строку: +We can also move a label into the separate string: ```js //+ no-beautify @@ -321,29 +352,34 @@ outer: for (var i = 0; i < 3; i++) { ... } ``` -Вызов `break outer` ищет ближайший внешний цикл с такой меткой и переходит в его конец. +The `continue` directive can also be used with a label. In this case the execution would jump onto the next iteration of the labelled loop. -В примере выше это означает, что будет разорван самый внешний цикл и управление перейдёт на `alert`. +[warn header="Labels are not Goto"] +Labels do not allow to jump into an arbitrary place of code. -Директива `continue` также может быть использована с меткой, в этом случае управление перепрыгнет на следующую итерацию цикла с меткой. +For example, it is impossible to do like this: +```js +break label; // jumps to label? No. -## Итого +label: for(...) +``` + +The call to a `break` is only possible from inside the loop, and the label must be somewhere upwards from the `break`. +[/warn] + +## Summary + +There are 3 types of loops in JavaScript: -JavaScript поддерживает три вида циклов: -Чтобы организовать бесконечный цикл, используют конструкцию `while(true)`. При этом он, как и любой другой цикл, может быть прерван директивой `break`. - -Если на данной итерации цикла делать больше ничего не надо, но полностью прекращать цикл не следует -- используют директиву `continue`. - -Обе этих директивы поддерживают "метки", которые ставятся перед циклом. Метки -- единственный способ для `break/continue` повлиять на выполнение внешнего цикла. - -Заметим, что метки не позволяют прыгнуть в произвольное место кода, в JavaScript нет такой возможности. - +To make in "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 more on this iteration and would like to forward on to the next one -- use the `continue` directive. +Both these directives support labels before the loop. A label is the only way for `break/continue` to go up the nesting and affect the outer loop.