Je passe en revue les bases de Kotlin et je suis assez confus avec cette fonction fold () et reduction () dans Kotlin. Quelqu'un peut-il me donner un exemple concret qui les distingue?
Réponses
Trop de publicités?fold
prend une valeur initiale, et la première invocation de la lambda de passer, il va recevoir la valeur initiale et le premier élément de la collection en tant que paramètres.
Prenez, par exemple, 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 à la lambda sera avec les paramètres 0
et 1
.
Avoir la capacité de transmettre en une valeur initiale est utile si vous avez à fournir une sorte de valeur par défaut ou d'un paramètre de votre opération. Par exemple, si vous avez été la recherche de la valeur maximale à l'intérieur d'une liste, mais pour une raison quelconque veux retourner au moins de 10, vous pouvez effectuer les opérations suivantes:
listOf(1, 6, 4).fold(10) { max, element ->
if (element > max) element else max
}
reduce
ne pas prendre une valeur initiale, mais au lieu de cela commence avec le premier élément de la collection que l'accumulateur (appelés sum
dans l'exemple suivant).
Par exemple, prenons-en une somme d'entiers de nouveau:
listOf(1, 2, 3).reduce { sum, element -> sum + element }
Le premier appel à la lambda ici seront, avec les paramètres 1
et 2
.
Vous pouvez utiliser reduce
lors de votre opération ne dépend pas des valeurs autres que celles de la collection que vous postulez pour.
La différence fonctionnelle majeure, je l'appellerais " (ce qui est mentionné dans les commentaires sur l'autre réponse, mais peut être difficile à comprendre) c'est que l' reduce
va lever une exception si elle est effectuée sur une zone vide de la collection.
listOf<Int>().reduce { x, y -> x + y }
// java.lang.UnsupportedOperationException: Empty collection can't be reduced.
C'est parce qu' .reduce
ne sait pas quelle est la valeur de retour en cas de "pas de données".
Cela contraste avec .fold
, ce qui vous oblige à fournir une "valeur de départ", qui sera la valeur par défaut en cas de vide de la collection:
val result = listOf<Int>().fold(0) { x, y -> x + y }
assertEquals(0, result)
Donc, même si vous ne voulez pas à s'agréger votre collection sur un seul élément d'un autre (non-relié) type (qui n' .fold
vous permettra de le faire), si votre départ de la collection peut être vide, alors vous devez vérifier votre taille de la collection en premier et ensuite .reduce
, ou simplement l'utiliser .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)