Какова основная разница между сбросом и сокращением в Котлине? Когда использовать какой?
Я разбираюсь в основах 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)