reduce

reduce permet d'itérer sur une séquence tout en stockant un résultat dans un accumulateur. Nous verrons ici que reduce permet de faire beaucoup, beaucoup de choses. Attention, comme map, reduce est lazy.

Un exemple simple

Prenons le code suivant:

(reduce + [1 2 3 4 5])

On voit ici que reduce prend 2 paramètres: une fonction (+ dans ce cas), ainsi qu'un vector. Ici, reduce retournera la somme de tous les éléments dans le vector, c'est à dire 15.

Le traitement réalisé par reduce peut être décrit de la manière suivante:

  • reduce va tout d'abord appeler la fonction + avec les deux premiers éléments du vector. Cela va donner
    (+ 1 2)
    Le résultat est donc 3.
  • Ensuite, reduce appellera la fonction avec comme premier paramètre le résultat précédent (3) suivi de l'élément suivant non traité du vector, c'est à dire le troisième élément (donc de valeur 3 également). Cela donnera
    (+ 3 3)
    Le résultat intermédiaire est donc maintenant 6.
  • Le calcul va continuer, 6 sera additionné à l'élément suivant (4) ce qui donnera 10, puis 15 lors de l'ajout du dernier élément de la liste.
  • reduce retourne ensuite le résultat: 15

Le code suivant:

(reduce #(+ %1 %2) [1 2 3 4 5])

est équivalent. On voit que l'on passe ici une fonction anonyme prenant deux paramètres pour réaliser la somme.

Valeur initiale

Il est possible de donner à reduce une valeur initiale qui sera utilisée lors du premier calcul. Par exemple, dans le code suivant:

(reduce + 30 [1 2 3 4 5])

Le premier calcul sera (+ 30 1) et non (+ 1 2) comme précédemment, car on utilise une valeur par défaut.

reduced

Il peut être parfois utile d'arrêter la reduction à un moment donné basé sur une condition. Par exemple:

(reduce (fn [acc elem] (if (> (+ acc elem) 5)
                                  (reduced elem)
                                  (+ acc elem)))
                 [1 2 3 4 5])

Ici, on vérifie dans la fonction si le nouveau résultat intermédiaire est supérieur à 5. Si oui, on arrête la réduction avec reduced et on retourne l'élément de la collection ayant provoqué la validité de la condition (le paramètre de reduced est la valeur retournée).

Ici, l'élément 3 est retourné, car en effet (+ 1 2 3) est supérieur à 5.

reduced permet aussi d'appeler reduce sur des séquences lazy infinies, et de stopper le calcul à un moment donné.

Conclusion

reduce est très utile et permet de faire beaucoup de choses. D'ailleurs, essayez de réimplémenter map ou filter avec reduce, cela est un bon exercice.

Précédent - Suivant -

;; sum of the element of a vector
(println (reduce + [1 2 3 4 5]))

;; same thing using an anonymous function
(println (reduce #(+ %1 %2) [1 2 3 4 5]))

;; Use an initial value
(println (reduce + 30 [1 2 3 4 5]))

;; Example of reduced: stop the computation if the accumulator is > 5
(println (reduce (fn [acc elem] (if (> (+ acc elem) 5)
                                  (reduced elem)
                                  (+ acc elem)))
                 [1 2 3 4 5]))

;; another example of reduce : filter non-even elements. In real world, you probably
;; want to use filter instead of reduce for this.
(println (reduce (fn [acc elem] (if (even? elem)
                                    (conj acc elem)
                                    acc))
                 []
                 [1 2 3 4 5]))

Powered by mcorbin - Available on Github