Программирование
Что произойдет, если заменить каждый цикл For в Swift на map, filter и reduce?
Цель была не в том, чтобы доказать, что функциональное программирование лучше, а в том, чтобы посмотреть, что произойдёт, если зайти слишком далеко.
Каждый разработчик 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 даёт нам оба мира. И настоящая сила заключается не в выборе одного, а в том, чтобы знать, когда использовать каждый из них.
-
Аналитика магазинов2 недели назад
Мобильный рынок Ближнего Востока: исследование Bidease и Sensor Tower выявляет драйверы роста
-
Интегрированные среды разработки3 недели назад
Chad: The Brainrot IDE — дикая среда разработки с играми и развлечениями
-
Новости4 недели назад
Видео и подкасты о мобильной разработке 2025.45
-
Новости3 недели назад
Видео и подкасты о мобильной разработке 2025.46

