Une réduction simple sur un tableau vide générera:
Exception dans le thread "principal" Java.lang.UnsupportedOperationException: l'itération vide ne peut pas être réduite.
La même exception lors du chaînage:
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
Est-ce le fonctionnement attendu de la réduction ou est-ce un bug?
Existe-t-il des solutions de contournement?
L'exception est correcte, reduce
ne fonctionne pas sur un itérable ou un tableau vide. Ce que vous recherchez probablement est fold
, qui prend une valeur de départ et une opération qui sont appliquées successivement pour chaque élément de l'itérable. reduce
prend l'élément first comme valeur de départ, il n'a donc pas besoin de passer de valeur supplémentaire comme argument, mais requiert que la collection ne soit pas vide.
Exemple d'utilisation de fold
:
println(intArrayOf().fold(0) { a, b -> a + b }) // prints "0"
Je veux juste ajouter une approche plus générale pour les situations où il n'est pas possible d'utiliser fold(...)
. Parce que, pour utiliser fold
, vous devez pouvoir exprimer une valeur initiale.
someIterable
.filter{ TODO("possibly filter-out everything") }
.takeIf{ it.isNotEmpty() }
?.reduce{ acc, element -> TODO("merge operation") }
?: TODO("value or exception for empty")
Avec cette approche, dans le cas d'une collection vide, reduce
ne sera pas exécuté car takeIf
le convertira en null
. Et à la fin, nous pouvons utiliser l'opérateur elvis pour exprimer une valeur (ou lever une exception) dans ce cas.
Votre exemple:
intArrayOf(1, 2, 3)
.filter({ a -> a < 0 })
.takeIf{ it.isNotEmpty() }
?.reduce({ a, b -> a + b })
?: 0
public inline fun <S, T : S> List<T>.reduceRightDefault(defaultIfEmpty: S, operation: (T, acc: S) -> S): S {
return if (isEmpty()) defaultIfEmpty
else reduceRight(operation)
}
usage:
val result = listOf<Boolean>().reduceRightDefault(false) { first, second -> first && second}
println("result $result")//result false
vous pouvez utiliser foldRight:
println(listOf("1", "2", "3")
.filter { "not found" == it }
.foldRight("") { a, b -> a + b }) // prints: ""
println(listOf("1", "2", "3")
.filter { "not found" != it }
.foldRight("") { a, b -> a + b }) // prints: "123"
ou dans votre cas:
val a = intArrayOf()
val b = a.foldRight(0) { memo, next -> memo + next } // b == 0
val a1 = intArrayOf(1, 2, 3)
val b1 = a.filter { a -> a < 0 }.foldRight(0) { a, b -> a + b } // b1 == 0