fold, reduce и агрегация
Учимся сворачивать коллекцию в одно значение.
fold сворачивает коллекцию в одно значение, последовательно комбинируя элементы, начиная с заданного начального значения.
Часто из коллекции нужно получить одно число или строку: сумму, произведение, объединённый текст. Такую операцию называют свёрткой (агрегацией), и главный инструмент здесь — fold.
fold — свёртка с начальным значением
fold принимает начальное значение (аккумулятор) и лямбду из двух аргументов: текущий аккумулятор и очередной элемент. Результат лямбды становится новым аккумулятором.
fun main() {
val numbers = listOf(1, 2, 3, 4)
val sum = numbers.fold(0) { acc, n -> acc + n }
println(sum)
val product = numbers.fold(1) { acc, n -> acc * n }
println(product)
}Вывод:
10 24
reduce — свёртка без начального значения
reduce похож на fold, но за начальное значение берёт первый элемент коллекции. Поэтому на пустой коллекции reduce бросает исключение, а fold вернёт начальное значение.
fun main() {
val numbers = listOf(3, 7, 2, 8)
val max = numbers.reduce { acc, n -> if (n > acc) n else acc }
println(max)
}Вывод:
8
Готовые агрегаты
Для частых задач есть готовые функции — их не нужно писать через fold вручную.
fun main() {
val numbers = listOf(5, 2, 9, 1)
println(numbers.sum())
println(numbers.max())
println(numbers.min())
println(numbers.average())
}Вывод:
17 9 1 4.25
Группировка
groupBy распределяет элементы по группам, образуя Map: ключ — результат лямбды, значение — список элементов с этим ключом.
fun main() {
val words = listOf("апельсин", "арбуз", "банан", "вишня")
val byFirst = words.groupBy { it.first() }
println(byFirst)
}Вывод:
{а=[апельсин, арбуз], б=[банан], в=[вишня]}Как работает под капотом
Внутри fold — обычный цикл, который перебирает элементы и на каждом шаге обновляет аккумулятор результатом лямбды. Тип аккумулятора может отличаться от типа элементов: например, можно свернуть список чисел в строку. Главное отличие fold от reduce — наличие явного начального значения, что делает fold безопасным для пустых коллекций.
Частые ошибки
- Применять
reduceк возможно пустой коллекции. На пустой коллекции он бросит исключение — беритеfoldс начальным значением. - Писать сумму через
foldвручную. Для частых случаев естьsum(),max(),average(). - Путать порядок аргументов лямбды. В
foldпервый аргумент — аккумулятор, второй — элемент.
Итог
foldсворачивает коллекцию в одно значение, начиная с заданного аккумулятора.reduceберёт первый элемент за начальное значение и падает на пустой коллекции.- Для частых задач есть
sum(),max(),average(). groupByраспределяет элементы по группам вMap.