web-dev-qa-db-fra.com

Comment appeler réduire sur un tableau Kotlin vide?

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?

35
dippe

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"
61
Alexander Udalov

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
2
Tomac Antonio
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
0
NickUnuchek

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
0
Maksim Kostromin