Я очень запутался в этих обеих функциях fold()
и reduce()
в Котлине, может ли кто-нибудь дать мне конкретный пример, который их отличает?
Я очень запутался в этих обеих функциях fold()
и reduce()
в Котлине, может ли кто-нибудь дать мне конкретный пример, который их отличает?
Ответы:
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
когда ваша операция не зависит от каких-либо значений, кроме значений в коллекции, к которой вы ее применяете.
emptyList<Int>().reduce { acc, s -> acc + s }
выдаст исключение, но emptyList<Int>().fold(0) { acc, s -> acc + s }
все в порядке.
listOf<Int>(1, 2).reduce { acc: Number, i: Int -> acc.toLong() + i }
(тип списка - Int, в то время как тип аккумулятора объявлен как Number и фактически является Long)
Основное функциональное различие, которое я бы назвал (которое упоминается в комментариях к другому ответу, но может быть трудно понять), заключается в том 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)