Как работает reduce в Swift
Что проверяют
Умеете ли вы пользоваться функциональными методами коллекций осознанно, а не ради краткости. Следом почти всегда спрашивают разницу между reduce(_:_:) и reduce(into:_:) и когда reduce хуже обычного цикла.
Как отвечать
reduce сворачивает последовательность в одно значение: берёт начальное значение и замыкание, которое получает накопленный результат и очередной элемент и возвращает новый результат. Проход идёт слева направо, сложность O(n), для пустой коллекции возвращается начальное значение. Варианта без начального значения, как в JavaScript, в стандартной библиотеке нет.
- простая свёртка:
numbers.reduce(0, +)для суммы, операторы передаются как функции; reduce(into:_:): аккумулятор передаётся какinoutи меняется на месте. Это правильный выбор, когда результат это массив, словарь или множество, например подсчёт частот:words.reduce(into: [String: Int]()) { $0[$1, default: 0] += 1 };rethrows: если замыкание бросает ошибку,reduceтоже бросает, и вызов пишется сtry.
Почему into важен: в варианте reduce([]) { $0 + [$1] } на каждом шаге создаётся новый массив и копируются все накопленные элементы, выходит квадратичная сложность. С reduce(into: []) и append буфер уникален и дописывается на месте.
Хороший ответ показывает и границы применимости. Для многих задач есть методы, которые читаются лучше: joined(separator:) для строк, max() и min(), Dictionary(grouping:by:) для группировки, allSatisfy и contains(where:) для проверок. Там, где нужен ранний выход, подойдёт for, потому что reduce всегда проходит коллекцию целиком.
Где спотыкаются
Склеивают строки через reduce("") { $0 + " " + $1 } и получают лишний пробел в начале. Для этого существует joined(separator: " ").
Собирают массивы и словари обычным reduce с копированием на каждом шаге. На паре десятков элементов незаметно, на десятках тысяч это ощутимая просадка.
Пишут внутри reduce многострочное замыкание с условиями и побочными эффектами. Такой код читается хуже цикла, и на ревью его обычно просят переписать.