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

TapanHP

Ответов: 5

Ответы (5)

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, если ваша операция не зависит от каких-либо значений, кроме тех, которые содержатся в коллекции, к которой вы ее применяете.

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

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

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

Сравните это с .fold, который требует, чтобы вы указали «начальное значение», которое будет значением по умолчанию в случае пустой коллекции:

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

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

val collection: List = // 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)

Простой ответ

Результатом сокращения и свертывания будет «список элементов будет преобразован в отдельный элемент".

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

Сложить

listOf("AC","Fridge").fold("stabilizer") { freeGift, itemBought -> freeGift + itemBought }

//output: stabilizerACFridge

В приведенном выше случае представьте себе кондиционер, холодильник, купленный в магазине, и они дают стабилизатор в качестве подарка (это будет параметр, переданный в сгибе). Итак, вы получите все 3 предмета вместе.

Уменьшить

В случае сокращения, мы получаем элементы в списке в качестве параметров и можем выполнять над ним необходимые преобразования.

listOf("AC","Fridge").reduce { itemBought1, itemBought2 -> itemBought1 + itemBought2 }

//output: ACFridge

Другое отличие, которое не упоминается ни в одном из других ответов, заключается в следующем:

Результат операции reduce всегда будет того же типа (или супертипа), что и сокращаемые данные. Мы видим, что из определения метода reduce:

public inline fun  Iterable.reduce(operation: (acc: S, T) -> S): S {
    val iterator = this.iterator()
    if (!iterator.hasNext()) throw UnsupportedOperationException("Empty collection can't be reduced.")
    var accumulator: S = iterator.next()
    while (iterator.hasNext()) {
        accumulator = operation(accumulator, iterator.next())
    }
    return accumulator
}

С другой стороны, результат операции сворачивания может быть любым, потому что нет никаких ограничений, когда дело доходит до установки начального значения. Так, например, предположим, что у нас есть строка, содержащая буквы и цифры. Мы хотим посчитать сумму всех цифр. Это легко сделать с помощью fold:

val string = "1a2b3"
val result: Int = string.fold(0, { currentSum: Int, char: Char ->
    if (char.isDigit())
        currentSum + Character.getNumericValue(char)
    else currentSum
})

//result is equal to 6

reduce - Метод reduce () преобразует данную коллекцию в единый результат .

val numbers: List = listOf(1, 2, 3)
val sum: Int = numbers.reduce { acc, next -> acc + next }
//sum is 6 now.

fold - Что произойдет в предыдущем случае с пустым списком? На самом деле, правильного значения для возврата нет, поэтому reduce () выдает RuntimeException

В этом случае fold - удобный инструмент. По нему можно поставить начальное значение -

val sum: Int = numbers.fold(0, { acc, next -> acc + next })

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

2022 WebDevInsider