Видеокурсы по изучению языка программирования Swift. Подробнее

Циклы While

Если вы нашли опечатку в тексте, выделите ее и нажмите CTRL + ENTER.

Цикл while выполняет набор инструкций до тех пор, пока его условие не станет false. Этот вид циклов лучше всего использовать в тех случаях, когда количество итераций до первого входа в цикл неизвестно. Swift предлагает два вида циклов while:

  • while - вычисляет условие выполнения в начале каждой итерации цикла.
  • repeat-while - вычисляет условие выполнения в конце каждой итерации цикла.

While

Цикл while начинается с вычисления условия. Если условие истинно, то инструкции в теле цикла будут выполняться до тех пора, пока оно не станет ложным.

Общий вида цикла while выглядит следующим образом:

  1. while условие {
  2. инструкции
  3. }

В этом примере показана простая игра Змеи и Лестницы (также известная, как Горы и Лестницы):

Игра проходит по следующим правилам:

  • Доска разделена на 25 квадратов и цель состоит в том, чтобы стать на 25-ый квадрат или за его пределами.
  • В начале каждого хода Вы бросаете игральную кость и перемещаетесь на то число шагов, которое выпало после броска, в направлении, которое указывает пунктирная стрелка.
  • Если ваш ход заканчивается на основании лестницы, то вы поднимаетесь по ней вверх.
  • Если ваш ход заканчивается на голове змеи, то вы спускаетесь вниз по этой змее.

Игровая доска в примере представлена массивом значений типа Int. Его размер хранится в константе finalSquare, которая используется как для инициализации массива, так и для проверки условия победы. Игровое поле инициализируется 26-ю, а не 25-ю целочисленными нулевыми значениями(каждое с индексом от 0 до 25 включительно):

let finalSquare = 25
var board = [Int](repeating: 0, count: finalSquare + 1)

Затем, для обозначения лестниц и змей, некоторым квадратам присваиваются специальные значения. Квадраты с основанием лестницы, перемещающие вас вверх по доске, имеют положительные значения, тогда как квадраты с головой змеи, спускающие вас вниз - отрицательное.

board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08

Квадрат 3 с основанием лестницы перемещает вас вверх на 11 квадрат. Чтобы это сделать, элементу массива board[03] присваивается +08, что эквивалентно значению 8 типа Int (разница между 3 и 11). Для того чтобы уточнить формулировку игрового поля, оператор унарного плюса (+i) уравновешивает оператор унарного минуса (-i), а числа ниже 10 приписаны нули. (В этих двух стилистический надстройках нет прямой необходимости, но они делают код более читаемым).

Все участники начинают игру с нулевого квадрата, который находится у левого нижнего края доски. Первый бросок кубика всегда перемещает участника на игровую доску:

var square = 0
var diceRoll = 0
while square < finalSquare {
  // бросок кубика
  diceRoll += 1
  if diceRoll == 7 { diceRoll = 1 }
  // начать ходить на выпавшее количество шагов
  square += diceRoll
  if square < board.count {
    // если мы все еще на поле, идти вверх или вниз по змеям или лестницам
    square += board[square]
  }
}
print("Game over!")

Данный пример использует самый простой подход к реализации броска кубика. Вместо использования генератора случайных чисел, значение diceRoll начинается с 0. Каждую итерацию цикла переменная diceRoll увеличивается на 1 с помощью инфиксного оператора (+= 1), после чего проверяется не стало ли её значение слишком большим. Возвращаемое значение += diceRoll равно значению переменной diceRoll после её инкрементирования. Когда это значение становится равным 7, оно сбрасывается на 1. В итоге мы получаем последовательность значений diceRoll, которая всегда будет выглядеть следующим образом: 1, 2, 3, 4, 5, 6, 1, 2 и так далее.

После броска кубика игрок перемещается вперед на количество клеток, равное значению переменной diceRoll. Возможен случай, когда бросок кубика может переместить игрока за пределы квадрата 25. В таком случае игра заканчивается. Для того чтобы справиться с таким сценарием, код проверяет что значение square меньше чем свойство count массива board перед прибавлением значения, хранящегося в board[square] к текущему значению square для перемещения игрока вверх или вниз по змеям или лестницам.

Заметка

 

Если бы этой проверки не было, могла бы произойти попытка обращения к значению board[square], находящемуся за границами массива board, что привело бы к вызову ошибки. Если square равно 26, код попытается проверить значение board[26], которое выходит за границы массива.

Текущая итерация цикла заканчивается, после чего проверяется условие цикла, для того чтобы понять нужно ли переходить к следующей итерации. Если игрок переместился на квадрат 25 или за его пределы, значение условия будет вычислено как false и игра закончится.

В данном случае использование while является наиболее подходящим, так как продолжительность игры неизвестна перед началом цикла. Цикл просто исполняется до тех пор, пока не будет выполнено конкретное условие.

Цикл repeat-while

Другой вариант цикла while, известный как цикл repeat-while, выполняет одну итерацию до того, как происходит проверка условия. Затем цикл продолжает повторяться до тех пока условие не станет false.

Заметка

 

Цикл repeat-while в Swift аналогичен циклу do-while в других языках. 

Общий вид цикла repeat-while выглядит следующим образом:

  1. repeat {
  2. инструкции
  3. } while условие

Ниже снова представлен пример игры Змеи и Лестницы , написанный с использованием цикла repeat-while. Значения переменных finalSquare, board, square и diceRoll инициализированы точно таким же образом, как и в случае с циклом while:

let finalSquare = 25
var board = [Int](count: finalSquare + 1, repeatedValue: 0)
board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
var square = 0
var diceRoll = 0

В этой версии игры в начале цикла происходит проверка на наличие змей или лестниц на квадрате. Ни одна лестница на поле не приведет игрока на квадрат 25. Таким образом невозможно победить в игре, переместившись вверх по лестнице. Следовательно, такая проверка в самом начале цикла является абсолютно безопасной.

В начале игры игрок находится на квадрате 0. board[0] всегда равняется 0 и не оказывает никакого влияния:

repeat {
  // идти вверх или вниз по змеям или лестницам
  square += board[square]
  // бросить кубик
  diceRoll += 1
  if diceRoll == 7 { diceRoll = 1 }
  // начать ходить на выпавшее количество шагов
  square += diceRoll
} while square < finalSquare
print("Game over!")

После проверки на наличие змей и лестниц происходит бросок кубика и игрок продвигается вперед на количество квадратов, равное diceRoll. После этого текущая итерация цикла заканчивается.

Условие цикла (while square < finalSquare) такое же, как раньше, но в этот раз оно не вычисляется до окончания первого запуска цикла. Структура цикла repeat-while лучше подходит для этой игры, чем цикл while в предыдущем примере. В цикле repeat-while выше square += board[square] всегда выполняется сразу, в то время как в цикле while происходит проверка того, что square все еще находится на поле. Такой принцип работы цикла repeat-while снимает необходимость проверки выхода за границы массива, которую мы видели в предыдущей версии игры.

Swift: 
3.0