Je suis assez confus avec ces deux fonctions fold()
et reduce()
dans Kotlin, quelqu'un peut-il me donner un exemple concret qui les distingue tous les deux?
Je suis assez confus avec ces deux fonctions fold()
et reduce()
dans Kotlin, quelqu'un peut-il me donner un exemple concret qui les distingue tous les deux?
Réponses:
fold
prend une valeur initiale, et la première invocation du lambda que vous lui passez recevra cette valeur initiale et le premier élément de la collection comme paramètres.
Par exemple, prenez le code suivant qui calcule la somme d'une liste d'entiers:
listOf(1, 2, 3).fold(0) { sum, element -> sum + element }
Le premier appel au lambda se fera avec les paramètres 0
et 1
.
Avoir la possibilité de transmettre une valeur initiale est utile si vous devez fournir une sorte de valeur ou de paramètre par défaut pour votre opération. Par exemple, si vous recherchez la valeur maximale dans une liste, mais que pour une raison quelconque, vous souhaitez en renvoyer au moins 10, vous pouvez effectuer les opérations suivantes:
listOf(1, 6, 4).fold(10) { max, element ->
if (element > max) element else max
}
reduce
ne prend pas de valeur initiale, mais commence à la place par le premier élément de la collection comme accumulateur (appelé sum
dans l'exemple suivant).
Par exemple, faisons à nouveau une somme d'entiers:
listOf(1, 2, 3).reduce { sum, element -> sum + element }
Le premier appel au lambda ici se fera avec les paramètres 1
et 2
.
Vous pouvez l'utiliser reduce
lorsque votre opération ne dépend d'aucune valeur autre que celles de la collection à laquelle vous l'appliquez.
emptyList<Int>().reduce { acc, s -> acc + s }
produira une exception, mais emptyList<Int>().fold(0) { acc, s -> acc + s }
c'est OK.
listOf<Int>(1, 2).reduce { acc: Number, i: Int -> acc.toLong() + i }
(le type de liste est Int tandis que le type d'accumulateur est déclaré comme Nombre et est en fait un Long)
La principale différence fonctionnelle que j'appellerais (qui est mentionnée dans les commentaires sur l'autre réponse, mais qui peut être difficile à comprendre) est qu'elle reduce
lèvera une exception si elle est effectuée sur une collection vide.
listOf<Int>().reduce { x, y -> x + y }
// java.lang.UnsupportedOperationException: Empty collection can't be reduced.
C'est parce .reduce
que ne sait pas quelle valeur renvoyer en cas de "pas de données".
Comparez ceci avec .fold
, qui vous oblige à fournir une "valeur de départ", qui sera la valeur par défaut en cas de collection vide:
val result = listOf<Int>().fold(0) { x, y -> x + y }
assertEquals(0, result)
Ainsi, même si vous ne souhaitez pas agréger votre collection en un seul élément d'un type différent (non lié) (ce .fold
qui vous permettra seulement de le faire), si votre collection de départ peut être vide, vous devez soit vérifier votre collection taille d'abord et ensuite .reduce
, ou utilisez simplement.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)