Каждый разработчик Swift обожает хороший цикл for. Это наш хлеб насущный. Нужно перебрать числа? Цикл for. Собрать данные? Цикл for. Изменить состояние? Да, снова цикл for.
Но я всё время слышал из «лагеря функционального программирования» шепоток о том, что map, filter и reduce могут заменить большинство циклов for — и не просто заменить их, а сделать их чище, быстрее и выразительнее.
Поэтому я задал себе простой вопрос:
А что, если я полностью откажусь от циклов
forв своей кодовой базе Swift и заменю их функциональными конструкциями?
Я решил попробовать. И результаты оказались… удивительными.
Правила эксперимента
Прежде чем погрузиться в работу, я установил несколько основных правил:
- Никаких циклов
forвообще - Это включает в себя
for-in,forEachи дажеwhile(жестоко, знаю) - Разрешены только функции высшего порядка —
map,filter,reduce,compactMap,flatMap - Использую настоящую кодовую базу
Мне не нужны были просто игрушечные примеры. Я провел этот эксперимент в небольшом приложении SwiftUI, которое я использую для отслеживания привычек.
Цель была не в том, чтобы доказать, что функциональное программирование лучше, а в том, чтобы посмотреть, что произойдёт, если зайти слишком далеко.
Разминка: простые преобразования
Начнём с малого.
Возведение чисел в квадрат
Версия цикла for:
var squares = [Int]()
for num in [1, 2, 3, 4] {
squares.append(num * num)
}
Версия map:
let squares = [1, 2, 3, 4].map { $0 * $0 }
Более чистый код. Однострочный. Без изменяемого массива. Мне уже нравится.
Фильтрация чётных чисел
Версия с циклом for:
var evens = [Int]()
for num in 1...10 {
if num % 2 == 0 {
evens.append(num)
}
}
Версия filter:
let evens = (1...10).filter { $0 % 2 == 0 }
Выразительный код. Читается как английский: «назовите мне числа, где число % 2 == 0».
Суммирование значений
Версия цикла for:
var total = 0
for num in [1, 2, 3, 4] {
total += num
}
Версия reduce:
let total = [1, 2, 3, 4].reduce(0, +)
Код короче. Но для новичков reduce(0, +) менее очевиден, чем num in ….
Пока что функциональный Swift побеждает. Но всё становится сложнее.
Где всё начало ломаться
Замена каждого цикла for приведёт к появлению… сомнительного кода.
Вложенные циклы
Рассмотрите возможность генерации всех пар чисел.
Версия с циклом for:
var pairs = [(Int, Int)]()
for i in 1...3 {
for j in 1...3 {
pairs.append((i, j))
}
}
Функциональная версия:
let pairs = (1...3).flatMap { i in
(1...3).map { j in (i, j) }
}
Работает. Умно. Но понятнее ли? Спорный вопрос.
Изменение состояния
Предположим, вы создаёте строку.
Версия цикла for:
var text = ""
for word in ["Swift", "is", "fun"] {
text += word + " "
}
Версия reduce:
let text = ["Swift", "is", "fun"].reduce("") { $0 + $1 + " " }
Кажется… навязанным. К тому же, reduce здесь занимает O(n²) из-за конкатенации строк. Так что цикл for может быть быстрее и безопаснее.
Ранний выход
Самая большая проблема: циклы, в которых вы прерываете или продолжаете выполнение.
for num in 1...10 {
if num == 5 { break }
print(num)
}
Внутри map нет элегантного break. Можно повозиться с prefix(while:), но это выглядит неестественно.
Производительность: циклы против Map/Filter/Reduce
Я провёл несколько тестов.
measure {
var sum = 0
for i in 1...1_000_000 {
sum += i
}
}
Против:
measure {
let sum = (1...1_000_000).reduce(0, +)
}
Результат: цикл for был немного быстрее (~10–15%), но оба были невероятно быстрыми.
Настоящая разница в производительности заключалась не во времени выполнения, а в ясности.
map/filterчасто делал код короче- но
reduceможет быть запутанным и сложным в отладке
Читаемость: моё честное мнение
После недели, проведенной в режиме только функционального кода:
- ✅ Более чистый код для простых преобразований
- Преобразование массивов в другие массивы? Идеальный вариант использования
- ✅ Меньше побочных эффектов
- Нет изменяющегося внешнего состояния внутри циклов
- ❌ Ужасно подходит для сложной логики.
- Всё с несколькими условиями, прерываниями или отслеживанием состояния становилось нечитаемым
- ❌ Отладка стала сложнее
- Нельзя просто так добавить
printв цепочкуreduce, не испортив её
Главный вывод
Итак… что произойдёт, если заменить все циклы for в Swift на map, filter и reduce?
- Ваш код будет выглядеть умнее
- Ваши коллеги могут вас возненавидеть
- И вы быстро усвоите золотое правило:
Используйте функциональные конструкции, когда они делают код понятнее. Используйте циклы
for, когда это не так.
По правде говоря, Swift даёт нам оба мира. И настоящая сила заключается не в выборе одного, а в том, чтобы знать, когда использовать каждый из них.

