reduce и агрегация
Другие способы объединять элементы.
«reduce и агрегация» — бесплатный урок Scala for Backend Engineering & Functional Programming на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Scala for Backend Engineering & Functional Programming, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Scala for Backend Engineering & Functional Programming содержит 4 уроков всего.
От свёртки к reduce
Иногда требуется объединить элементы без отдельного начального значения.
reduce использует первый элемент как начальный аккумулятор и объединяет с ним остальные.
Это более лаконичный родственник fold для случаев, когда тип элементов и тип результата совпадают.
Основы reduce
reduce принимает бинарную функцию (a, b) и применяет её ко всей коллекции без явно заданного начального значения.
Для суммы он просто складывает все элементы.
val xs = List(1, 2, 3, 4)
val total = xs.reduce((a, b) => a + b)
@main def run(): Unit =
println(total) // 10Проблема пустой коллекции
Поскольку у reduce нет начального значения, вызов для пустой коллекции выбрасывает UnsupportedOperationException.
fold и foldLeft безопасны для пустых коллекций, поскольку используют предоставленное начальное значение.
Используйте reduce только тогда, когда уверены, что коллекция непуста.
// List.empty[Int].reduce(_ + _)
// throws UnsupportedOperationException
List.empty[Int].foldLeft(0)(_ + _) // safe, returns 0Безопасность с reduceOption
reduceOption возвращает Option, выдавая None для пустой коллекции вместо создания ошибки.
Это безопасный способ выполнить свёртку, если коллекция может оказаться пустой.
val xs = List(5, 9, 2)
val maybeMax = xs.reduceOption(_ max _)
@main def run(): Unit =
println(maybeMax) // Some(9)reduceLeft и reduceRight
Как и у свёртки, у reduce есть варианты с направлением.
reduceLeft объединяет элементы слева направо, а reduceRight — справа налево. Обычный reduce не гарантирует порядок для параллельных коллекций, поэтому, когда направление важно, используйте явный вариант.
val xs = List(1, 2, 3)
val l = xs.reduceLeft(_ - _) // (1-2)-3 = -4
val r = xs.reduceRight(_ - _) // 1-(2-3) = 2
@main def run(): Unit =
println((l, r)) // (-4, 2)Зачем нужен aggregate
fold и reduce требуют, чтобы типы аккумулятора и элементов были связаны простым образом.
aggregate — наиболее общий вариант свёртки: он позволяет использовать аккумулятор другого типа AND указывает Scala, как объединять частичные аккумуляторы, что важно при параллельной обработке.
Сигнатура aggregate
aggregate принимает начальное значение, операцию свёртки элемента в аккумулятор и операцию объединения двух аккумуляторов.
В последовательной коллекции операция объединения используется редко, а в параллельной соединяет результаты обработки каждого фрагмента.
val xs = List(1, 2, 3, 4)
val sum = xs.aggregate(0)(
(acc, x) => acc + x, // seqop
(a, b) => a + b // combop
)
@main def run(): Unit = println(sum) // 10aggregate с другим типом
Здесь aggregate сворачивает список слов в общую длину типа Int.
Операция свёртки добавляет длину каждого слова, а операция объединения складывает два частичных результата.
Тип аккумулятора (Int) отличается от типа элементов (String).
val words = List("hi", "there", "you")
val chars = words.aggregate(0)(
(acc, w) => acc + w.length,
(a, b) => a + b
)
@main def run(): Unit = println(chars) // 10Параллельная агрегация
Настоящая сила aggregate проявляется при работе с параллельными коллекциями.
Каждый поток сворачивает свой фрагмент, затем фрагменты объединяются. Эти две функции позволяют Scala безопасно разделять и снова объединять работу.
// Conceptual: par splits the work
// val n = data.par.aggregate(0)(_ + _.length, _ + _)
// seqop runs per chunk, combop merges chunk resultsВыбор подходящего инструмента
Используйте reduce или reduceOption, когда тип результата совпадает с типом элементов, а объединение ассоциативно.
Используйте foldLeft, когда в последовательной обработке нужны начальное значение или другой тип результата.
Используйте aggregate, когда типы аккумулятора и элементов различаются и Вам нужно объединение, подходящее для параллельной обработки.
// reduce: same type, non-empty, associative
// foldLeft: seed + different type, sequential
// aggregate: different type + parallel mergeОбъединение за один проход
aggregate может вычислять несколько значений одновременно, накапливая их в кортеже.
Здесь за один проход мы получаем и сумму, и количество элементов, объединяя кортежи операцией объединения.
val xs = List(2, 4, 6, 8)
val (s, c) = xs.aggregate((0, 0))(
(acc, x) => (acc._1 + x, acc._2 + 1),
(a, b) => (a._1 + b._1, a._2 + b._2)
)
@main def run(): Unit = println((s, c)) // (20, 4)Быстрая проверка
Определите, какая операция безопаснее всего для списка, который может оказаться пустым.
Итоги
reduce объединяет элементы без начального значения, используя первый элемент как начало; для пустой коллекции он создаёт ошибку, а reduceOption вместо этого возвращает None.
aggregate — наиболее общий вариант свёртки: начальное значение, операция свёртки элементов и операция объединения частичных аккумуляторов для параллельной работы.
Выбирайте reduce для ассоциативного объединения значений одного типа, foldLeft — для последовательной свёртки с начальным значением, а aggregate — когда типы различаются или обработка выполняется параллельно.
Часто задаваемые вопросы
Урок «reduce и агрегация» бесплатный?
Да — полный текст урока «reduce и агрегация» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Scala for Backend Engineering & Functional Programming, подпишись на CoddyKit PRO. Курс Scala for Backend Engineering & Functional Programming содержит 4 уроков всего.
Чему я научусь в уроке «reduce и агрегация»?
Другие способы объединять элементы. Ты практикуешь Scala for Backend Engineering & Functional Programming с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Scala for Backend Engineering & Functional Programming?
Предыдущий опыт не требуется. Scala for Backend Engineering & Functional Programming на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.
Сколько времени занимает урок «reduce и агрегация»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Scala for Backend Engineering & Functional Programming?
Да. Каждый урок Scala for Backend Engineering & Functional Programming включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Рекурсивное мышление
- Шаблоны с аккумулятором
- foldLeft и foldRight
- reduce и агрегация