Взгляните на это для глубокого фундаментального обсуждения этой темы
GhostCat
2
@LunarWatcher, я видел эти документы, но не понял, это вопрос, который вы задали, можете ли вы привести пример?
TapanHP
1
@MattKlein done
Джейсон Минард
Ответы:
281
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когда ваша операция не зависит от каких-либо значений, кроме значений в коллекции, к которой вы ее применяете.
Хорошее объяснение! Я бы сказал также, что пустую коллекцию нельзя уменьшить, но можно свернуть.
Miha_x64 08
Видите ли, я на очень начальном уровне в Котлине, самый первый пример, который вы привели, можете ли вы объяснить его подробнее с помощью некоторых шагов и окончательного ответа? было бы большим подспорьем
TapanHP 08
3
@TapanHP emptyList<Int>().reduce { acc, s -> acc + s }выдаст исключение, но emptyList<Int>().fold(0) { acc, s -> acc + s }все в порядке.
Miha_x64 08
31
reduce также заставляет возвращать лямбда того же типа, что и члены списка, что неверно для fold. Это важное следствие создания первого элемента списка, начального значения аккумулятора.
andresp
4
@andresp: просто в качестве примечания для полноты: он не обязательно должен быть одного типа. Члены списка также могут быть подтипом аккумулятора: это действительно работает listOf<Int>(1, 2).reduce { acc: Number, i: Int -> acc.toLong() + i }(тип списка - Int, в то время как тип аккумулятора объявлен как Number и фактически является Long)
Борис
11
Основное функциональное различие, которое я бы назвал (которое упоминается в комментариях к другому ответу, но может быть трудно понять), заключается в том 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 sizeval result1=if(collection.isEmpty())0else collection.reduce { x, y -> x + y }val result2= collection.fold(0){ x, y -> x + y }
assertEquals(result1, result2)
Ответы:
fold
принимает начальное значение, и при первом вызове переданной ему лямбды будет получено это начальное значение и первый элемент коллекции в качестве параметров.Например, возьмите следующий код, который вычисляет сумму списка целых чисел:
Первый вызов лямбды будет с параметрами
0
и1
.Возможность передачи начального значения полезна, если вам нужно предоставить какое-то значение или параметр по умолчанию для вашей операции. Например, если вы ищете максимальное значение внутри списка, но по какой-то причине хотите вернуть не менее 10, вы можете сделать следующее:
reduce
не принимает начальное значение, а вместо этого начинается с первого элемента коллекции в качестве аккумулятора (вызываемогоsum
в следующем примере).Например, давайте снова просуммируем целые числа:
Первый вызов лямбды здесь будет с параметрами
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
, что при выполнении с пустой коллекцией возникает исключение .Причина в том, что
.reduce
неизвестно, какое значение вернуть в случае отсутствия данных.Сравните это с тем
.fold
, что вам необходимо указать «начальное значение», которое будет значением по умолчанию в случае пустой коллекции:Итак, даже если вы не хотите агрегировать свою коллекцию до одного элемента другого (не связанного) типа (что только
.fold
позволит вам это сделать), если ваша начальная коллекция может быть пустой, вы должны либо проверить свою коллекцию сначала размер, а затем.reduce
или просто используйте.fold
источник