web-dev-qa-db-fra.com

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

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?

84
TapanHP

fold prend une valeur initiale et le premier appel du lambda que vous lui transmettez recevra cette valeur initiale et le premier élément de la collection en tant que paramètres.

Par exemple, prenons 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 sera avec les paramètres 0 et 1.

Il est utile de pouvoir transmettre une valeur initiale 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 vous souhaitez renvoyer au moins 10, vous pouvez procéder comme suit:

listOf(1, 6, 4).fold(10) { max, element ->
    if (element > max) element else max
}

reduce ne prend pas de valeur initiale, mais commence par le premier élément de la collection en tant qu'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 sera ici avec les paramètres 1 et 2.

Vous pouvez utiliser reduce lorsque votre opération ne dépend pas de valeurs autres que celles de la collection à laquelle vous l'appliquez.

182
zsmb13

La différence fonctionnelle majeure que j'appellerais (qui est mentionnée dans les commentaires de l'autre réponse, mais peut être difficile à comprendre) est que reducelèvera une exception si elle est exécutée sur une collection.

listOf<Int>().reduce { x, y -> x + y }
// Java.lang.UnsupportedOperationException: Empty collection can't be reduced.

En effet, .reduce ne sait pas quelle valeur renvoyer en cas de "pas de données".

Contrastez ceci avec .fold, ce qui vous oblige à fournir une "valeur de départ", qui sera la valeur par défaut dans le cas d'une collection vide:

val result = listOf<Int>().fold(0) { x, y -> x + y }
assertEquals(0, result)

Donc, même si vous ne voulez pas agréger votre collection en un seul élément d'un type différent (non lié) (que seul .fold vous laissera faire), si votre collection de départ est vide, Vous devez d'abord vérifier la taille de votre collection, puis .reduce, ou simplement 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)
3
Matt Klein