Перейти к содержимому
Теория · Массивы · урок 5 из 8

Перебор массива

Представьте, что вы идёте вдоль ряда шкафчиков и открываете их по очереди, от первого до последнего. В каждом вы что-то делаете: пересчитываете вещи или ищете нужную. Цикл for-in умеет так же проходить по массиву.

Вот программа, которая складывает все оценки:

SWIFT
let scores = [7, 9, 4]
var total = 0
for score in scores {
    total += score
}
print(total)  // 20

Давайте разберём её по шагам:

  1. for score in scores на каждой итерации кладёт в константу цикла score следующий элемент: сначала 7, потом 9, потом 4.
  2. Тело прибавляет элемент к total.
  3. Элементы кончились, цикл завершился, и print печатает 20.

Индексы здесь не нужны вовсе. Цикл сам идёт по элементам по порядку и никогда не выходит за границы массива.

Здесь картинка с рядом шкафчиков неточна. Открыв шкафчик, вы можете переложить в нём вещь. А score — константа цикла с копией элемента, и через неё массив не изменить.

Номер и элемент вместе

Иногда нужен и элемент, и его индекс: например, чтобы напечатать место участника рядом с набранными им очками. Для этого есть два способа.

enumerated()

scores.enumerated() даёт на каждой итерации кортеж из двух частей: номер элемента и сам элемент. Запись (i, score) разбирает кортеж на два имени, как let (a, b) = … в теме про функции. Что важно запомнить:

  • первым идёт номер, вторым — элемент;
  • номера начинаются с 0, как индексы;
  • если одна часть не нужна, вместо её имени пишут _: for (i, _) in scores.enumerated().

indices

scores.indices — все допустимые индексы массива, от 0 до count - 1. Цикл по ним даёт только номера, а элемент читают по индексу: scores[i]. По индексу элемент можно и заменить: если массив объявлен через var, scores[i] *= 2 в таком цикле удвоит каждый элемент.

SWIFT
for (i, score) in scores.enumerated() {
    print(i, score, terminator: "; ")  // 0 7; 1 9; 2 4;
}
print()
for i in scores.indices {
    print(i, terminator: " ")  // 0 1 2
}
print()

Этот вопрос часто задают на собеседовании: с какого числа enumerated() начинает счёт и в каком порядке идут части кортежа. Ответ: с нуля, сначала номер, потом элемент.

Порядок

sorted()

scores.sorted() возвращает новый массив, в котором элементы стоят по возрастанию. Сам scores остаётся прежним.

reversed()

scores.reversed() даёт элементы в обратном порядке, но не новый массив: print(scores.reversed()) не напечатает [4, 9, 7]. Поэтому по таким элементам проходят циклом for-in. Сам массив и здесь не меняется.

SWIFT
print(scores.sorted(), scores)  // [4, 7, 9] [7, 9, 4]
for score in scores.reversed() {
    print(score, terminator: " ")  // 4 9 7
}
print()

Частая ошибка

Ждали, что sorted() отсортирует сам массив:

SWIFT
var marks = [3, 1, 2]
marks.sorted() // ✗
print(marks)  // [3, 1, 2]
TEXT
warning: result of call to 'sorted()' is unused

Программа запускается, но печатает [3, 1, 2]. sorted() построил новый отсортированный массив, и его никто не сохранил. Компилятор об этом и предупреждает: результат вызова не используется. У remove(at:) такого предупреждения нет: он сам меняет массив, и удалённый элемент можно не забирать.

Исправить можно двумя способами:

  • сохранить результат: let sortedMarks = marks.sorted();
  • если marks объявлен через var, отсортировать сам массив методом sort(): после marks.sort() в marks лежит [1, 2, 3].

Главное

  • for x in array проходит элементы по порядку и не выходит за границы массива.
  • enumerated() даёт кортеж «номер, элемент» с номерами от 0, а indices — все допустимые индексы.
  • sorted() возвращает новый массив, а sort() сортирует сам массив, объявленный через var; reversed() массив не меняет.