Как вызвать сокращение на пустой массив 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