Quelle est la différence fondamentale entre plier et réduire dans Kotlin? Quand utiliser lequel?


131

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?



4
Jetez un œil à ceci pour une discussion fondamentale approfondie sur ce sujet
GhostCat

2
@LunarWatcher, j'ai vu ces documents, mais je ne les comprends pas, c'est une question posée, pouvez-vous donner un exemple?
TapanHP

1
@MattKlein done
Jayson Minard

Réponses:


281

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 0et 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
}

reducene prend pas de valeur initiale, mais commence à la place par le premier élément de la collection comme accumulateur (appelé sumdans 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 1et 2.

Vous pouvez l'utiliser reducelorsque votre opération ne dépend d'aucune valeur autre que celles de la collection à laquelle vous l'appliquez.


47
Bonne explication! Je dirais aussi que cette collection vide ne peut pas être réduite, mais peut être pliée.
Miha_x64

voyez, m au niveau très débutant dans Kotlin, le tout premier exemple que vous avez donné pouvez-vous l'expliquer plus avec quelques étapes, et la réponse finale? serait d'une grande aide
TapanHP

3
@TapanHP emptyList<Int>().reduce { acc, s -> acc + s }produira une exception, mais emptyList<Int>().fold(0) { acc, s -> acc + s }c'est OK.
Miha_x64

31
Réduire force également le retour du lambda à être du même type que les membres de la liste, ce qui n'est pas vrai avec fold. C'est une conséquence importante de faire du premier élément de la liste, la valeur initiale de l'accumulateur.
andresp

4
@andresp: juste comme une note pour l'exhaustivité: il ne doit pas être du même type. Les membres de la liste peuvent également être un sous-type de l'accumulateur: cela fonctionne 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)
Boris

11

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 .reduceque 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 .foldqui 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)
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.