Цикл for-in
Чему вы научитесь:
- повторять код циклом
for-inпо диапазону и по ряду чисел изstride; - выбирать между
whileиrepeat-whileи предсказывать, сколько раз выполнится тело цикла; - выходить из цикла через
breakи пропускать круг черезcontinue; - выходить сразу из двух вложенных циклов через
breakс меткой.

Чтобы напечатать номера кругов с первого по сотый, пришлось бы написать сто строк с print. Так никто не делает. Для повторяющихся действий есть цикл — конструкция, которая выполняет блок кода снова и снова.
Представьте беговую дорожку. Тренер говорит бегуну: «Пробеги круги с первого по третий». Бегун пробегает первый круг, потом второй, потом третий. Когда круги из задания кончились, тренировка завершена.
Цикл for-in работает так же. Ему заранее называют номера кругов, и он выполняет свой блок кода — тело цикла — по разу для каждого номера:
for lap in 1...3 {
print("lap", lap, terminator: "; ") // lap 1; lap 2; lap 3;
}Давайте разберём, что здесь происходит:
1...3— закрытый диапазон из темы про операторы: числа1,2и3.- На первом круге в
lapлежит1, и тело печатаетlap 1. - На втором круге в
lapлежит2, на третьем —3. - Номера кончились, и цикл завершается.
terminator: "; " ставит после каждого круга точку с запятой вместо перевода строки, поэтому все три круга печатаются в одну строку.
Как устроен for-in
Заголовок for lap in 1...3 читается как «для каждого lap из 1...3». Его части:
for— ключевое слово, с которого начинается цикл;lap— имя, под которым тело видит номер текущего круга;inи диапазон после него — номера, которые цикл пройдёт по порядку;- фигурные скобки — тело цикла, то есть код, который повторяется.
Один проход тела называют итерацией. Цикл for lap in 1...3 делает три итерации, по одной на каждое число диапазона.
Полуоткрытый диапазон тоже подходит. Цикл for i in 0..<3 выполнит тело для 0, 1 и 2: так удобно повторить действие ровно три раза.
Номер круга нельзя изменить
Имя lap ведёт себя как константа let: на каждой итерации Swift создаёт его заново и кладёт в него следующий номер. Изменить его нельзя: если написать в теле lap += 1, программа не скомпилируется.
error: left side of mutating operator isn't mutable: 'lap' is a 'let' constantЗдесь картинка с дорожкой неточна. Живой бегун может передумать и пробежать лишний круг. У for-in номера кругов заданы ещё до старта, и тело цикла их не меняет.
Имя lap живёт только внутри фигурных скобок. Строка print(lap) после цикла даст ошибку cannot find 'lap' in scope.
Когда номер не нужен
Иногда тело не использует номер круга. Тогда вместо имени пишут _ — знак «значение мне не нужно»:
var meters = 0
for _ in 1...4 {
meters += 400
}
print(meters) // 1600Бегун пробежал четыре круга по 400 метров, и программа печатает 1600. Номер конкретного круга здесь ни к чему: важно только, сколько кругов.
Если написать for i in 1...4 и не использовать i в теле, программа запустится, но компилятор предупредит:
warning: immutable value 'i' was never used; consider replacing with '_' or removing itШаг больше единицы
Диапазон всегда идёт с шагом 1. Чтобы перебрать числа через одно, нужен stride: он строит ряд чисел от начала с заданным шагом. После from: пишут начало ряда, после by: — шаг, а конец задают одним из двух способов:
for n in stride(from: 0, to: 10, by: 2) {
print(n, terminator: " ") // 0 2 4 6 8
}
print()
for n in stride(from: 0, through: 10, by: 2) {
print(n, terminator: " ") // 0 2 4 6 8 10
}Пустой print() печатает только перевод строки, поэтому второй ряд начинается с новой.
stride(from:to:by:)
С to: конец в ряд не входит, как у полуоткрытого диапазона ..<. Ряд от 0 с шагом 2 доходит до 8. Следующее число 10 равно концу, поэтому в ряд не попадает.
stride(from:through:by:)
С through: конец входит в ряд, как у закрытого диапазона ..., поэтому второй цикл печатает и 10. Если шаг не попадает точно в конец, ряд останавливается раньше: stride(from: 0, through: 9, by: 2) даёт 0 2 4 6 8.
Запомнить разницу помогает перевод: to значит «до», а through — «до конца включительно».
Частая ошибка
Обратный отсчёт написали через диапазон:
for n in 3...1 { // ✗
print(n)
}Fatal error: Range requires lowerBound <= upperBoundПрограмма компилируется, но при запуске аварийно завершается ещё до первой итерации: начало диапазона не может быть больше конца. Для обратного отсчёта нужен stride с отрицательным шагом: stride(from: 3, through: 1, by: -1) даёт 3 2 1.
Главное
for-inвыполняет тело по разу для каждого числа диапазона или рядаstride, и каждый такой проход — итерация.- Имя из заголовка цикла — константа: она получает новое значение на каждой итерации, и менять её в теле нельзя.
_ставят вместо имени, когда значение не нужно. stride(from:to:by:)не включает конец, аstride(from:through:by:)включает его, если шаг на него попадает.- Обратный отсчёт — это
strideс отрицательным шагом: диапазон3...1аварийно завершает программу.