Какова основная разница между сбросом и сокращением в Котлине? Когда использовать какой?

Я разбираюсь в основах Kotlin, и я довольно смущен этими обеими функциями fold() и reduce() в Котлине, может ли кто-нибудь дать мне конкретный пример, который отличает их обоих?

Ответы

Ответ 1

fold принимает начальное значение, и первый вызов лямбды, который вы передаете ему, получит это начальное значение и первый элемент коллекции как параметры.

Например, возьмите следующий код, который вычисляет сумму списка целых чисел:

listOf(1, 2, 3).fold(0) { sum, element -> sum + element }

Первый вызов лямбда будет с параметрами 0 и 1.

Наличие возможности передать начальное значение полезно, если вам нужно предоставить какое-то значение по умолчанию или параметр для вашей операции. Например, если вы искали максимальное значение внутри списка, но по какой-то причине хотите вернуть не менее 10, вы можете сделать следующее:

listOf(1, 6, 4).fold(10) { max, element ->
    if (element > max) element else max
}

reduce не принимает начальное значение, но вместо этого начинается с первого элемента коллекции в качестве аккумулятора (называемого sum в следующем примере).

Например, пусть снова сделайте сумму целых чисел:

listOf(1, 2, 3).reduce { sum, element -> sum + element }

Первый вызов лямбда здесь будет с параметрами 1 и 2.

Вы можете использовать reduce, когда ваша операция не зависит от каких-либо значений, отличных от тех, в которых вы применяете ее.

Ответ 2

Основное функциональное различие, которое я бы назвал (упомянутое в комментариях к другому ответу, но может быть трудным для понимания), заключается в том, что reduce вызывает исключение, если выполняется в пустом списке.

listOf<Int>().reduce { x, y -> x + y }
// java.lang.UnsupportedOperationException: Empty collection can't be reduced.

Это потому, что reduce не знает, какое значение вернуть в случае "отсутствия данных".

Сравните это с .fold, который требует от вас предоставления "начального значения"

val result = listOf<Int>().fold(0) { x, y -> x + y }
assertEquals(0, result)

Таким образом, даже если вы не хотите объединять свою коллекцию в один элемент другого типа (что позволит сделать только .fold), если ваша начальная коллекция может быть пустой, то сначала необходимо проверить размер коллекции и затем .reduce, или просто используйте .fold

val collection: List<Int> = // collection of unknown size

val result1 = if (collection.isEmpty()) 0
              else collection.reduce { x, y -> x + y }

val result2 = collection.fold(0) { x, y -> x + y }

assertEquals(result1, result2)