Я дуже плутаю це з обома функціями 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, тоді як тип акумулятора оголошено як число і насправді є довгим)
Основна функціональна відмінність, яку я хотів би зауважити (про яку йдеться в коментарях до іншої відповіді, але може бути важко зрозуміти), полягає в тому, що 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)