Как вызвать сокращение на пустой массив Kotlin?
Простое сокращение на пустом массиве будет бросать:
Исключение в потоке "main" java.lang.UnsupportedOperationException: Empty iterable не может быть уменьшен.
Такое же исключение при цепочке:
val a = intArrayOf()
val b = a.reduce({ memo, next -> memo + next }) // -> throws an exception
val a1 = intArrayOf(1, 2, 3)
val b1 = a.filter({ a -> a < 0 }).reduce({ a, b -> a + b }) // -> throws an exception
Является ли это ожидаемой операцией сокращения или это ошибка?
Есть ли способы обхода?
Ответы
Ответ 1
Исключение корректно, reduce
не работает на пустой итерабельный или массив. Вероятно, вы ищете fold
, который принимает начальное значение и операцию, которая применяется последовательно для каждого элемента итерации. reduce
берет первый элемент как начальное значение, поэтому ему не нужно передавать какое-либо дополнительное значение в качестве аргумента, но требует, чтобы коллекция была не пустой.
Пример использования fold
:
println(intArrayOf().fold(0) { a, b -> a + b }) // prints "0"
Ответ 2
Я просто хочу добавить более общий подход для ситуаций, когда невозможно использовать fold(...)
. Потому что, чтобы использовать fold
, вы должны иметь возможность выразить некоторое начальное значение.
someIterable
.filter{ TODO("possibly filter-out everything") }
.takeIf{ it.isNotEmpty() }
?.reduce{ acc, element -> TODO("merge operation") }
?: TODO("value or exception for empty")
При таком подходе, в случае пустой коллекции, reduce
не будет выполнен, поскольку takeIf
преобразует его в null
. И в конце мы можем использовать оператор elvis для выражения некоторого значения (или выбросить исключение) в этом случае.
Ваш пример:
intArrayOf(1, 2, 3)
.filter({ a -> a < 0 })
.takeIf{ it.isNotEmpty() }
?.reduce({ a, b -> a + b })
?: 0