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?
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.
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 reducelè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 sizeval result1=if(collection.isEmpty())0else collection.reduce { x, y -> x + y }val result2= collection.fold(0){ x, y -> x + y }
assertEquals(result1, result2)
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:
Le premier appel au lambda se fera avec les paramètres
0
et1
.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:
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:
Le premier appel au lambda ici se fera avec les paramètres
1
et2
.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.la source
emptyList<Int>().reduce { acc, s -> acc + s }
produira une exception, maisemptyList<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.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: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
la source