Venez découvrir le concept de Fold !
Hérité de la programmation fonctionnelle, le Fold abstrait l'itération sur vos structures de données, et améliore l'expressivité de votre code.
Avec de vrais bouts de Java 8 et une pincée de Haskell !
4. FOLD ?
Définition en Haskell
foldl :: (a → b → a) → a → [b] → a
… Pas de panique !
5. FOLD ?
Exercice 1
Soit une liste d'entiers
→ Les additionner
List<Integer> nums = Arrays.asList(1,2,3,4,5);
6. FOLD ?
Exercice 1
Soit une liste d'entiers
→ Les additionner
List<Integer> nums = Arrays.asList(1,2,3,4,5);
public Integer sum(List<Integer> nums) {
Integer sum = 0;
for (Integer num : nums) {
sum = sum + num;
}
return sum;
}
7. FOLD ?
Exercice 2
Soit une liste d'entiers
→ Les multiplier
List<Integer> nums = Arrays.asList(1,2,3,4,5);
8. FOLD ?
Exercice 2
Soit une liste d'entiers
→ Les multiplier
List<Integer> nums = Arrays.asList(1,2,3,4,5);
public Integer product(List<Integer> nums) {
Integer product = 1;
for (Integer num : nums) {
product = product * num;
}
return product;
}
9. FOLD ?
Pattern commun
Accumulateur ← valeur initiale
Boucle sur la liste
Opération (accumulateur, élément)
public Integer foo(List<Integer> nums) {
Integer accu = <init>;
for (Integer num : nums) {
accu = accu <op> num;
}
return accu;
}
10. FOLD ?
Type de l'accumulateur
Type des éléments de la liste
public <A, E> A fold (BiFunction<A, E, A> op,
A init,
List<E> list) {
A accu = init;
for (E num : list) {
accu = op.apply(accu, num);
}
return accu;
}
11. FOLD ?
BiFunction<Integer,Integer,Integer> plus =
new BiFunction<>() {
public Integer apply(Integer accu, Integer elem) {
return accu + elem;
}
};
BiFunction<Integer,Integer,Integer> mult =
new BiFunction<>() {
public Integer apply(Integer accu, Integer elem) {
return accu * elem;
}
};
Integer sum
= fold (plus, 0, nums);
Integer product = fold (mult, 1, nums);
13. FOLD ?
Java 8 : Références de méthodes
Class::staticFunction
Integer sum = fold (Integer::plus, 0, nums);
public class MathUtil {
public static Integer mult(Integer x,Integer y) {
return x * y;
}
}
Integer prod = fold (MathUtil::mult, 1, nums);
14. FOLD ?
Définition en Haskell
foldl :: (a → b → a) → a → [b] → a
fonction
(a,b) → a
accumulateur
de type a
résultat
liste<b>
15. PRINCIPES
Définition
“
En programmation fonctionnelle, l'opération fold
(ou reduce) est une famille de fonctions d'ordre
supérieur qui traitent une structure de données
dans un certain ordre pour produire un résultat.
– http://www.haskell.org/haskellwiki/Fold
16. PRINCIPES
Avantages
Mécanisme très générique
Fonction d'ordre supérieur
Encapsulation de l'itération
Expressivité ("quoi" vs "comment")
Optimisation des opérations associatives
Famille de fonctions
foldl, foldr, foldl1, foldr1
scanl, scanr, scanl1, scanr1
19. PRINCIPES
Fold left vs Fold right
Opérations non commutatives
(soustraction, division...)
foldl
foldr
(-)
(-)
0
0
[1..5]
[1..5]
quiz !
Performances
Plus efficace d'ajouter en tête des listes
→ foldr : élément à gauche, liste à droite
20. PRINCIPES
Autres folds
foldl1, foldr1
Même principe que foldl et foldr
Accumulateur ← 1° élément de la liste
foldl (+) 0 [1..5]
foldl1 (+) X [1..5]
valeur initiale implicite
21. PRINCIPES
Autres folds
scanl, scanr
scanl1, scanr1
Même principe
Renvoient toutes les valeurs intermédiaires
scanl (+) 0 [1..5]
[0,1,3,6,10,15]
scanr (+) 0 [1..5]
[15,14,12,9,5,0]
22. USAGES
Folds complexes
Fold sert de base à beaucoup d'algorithmes
impliquant le parcours d'une liste
Le résultat peut être une valeur unique ("reduce")
ou une autre liste !
Dépend de l'accumulateur et de l'opération
On peut effectuer une opération complexe
par composition de fonctions simples
f(g(x)) ↔ (f ⋅ g)(x)
23. USAGES
Fonction map
Applique une fonction f à chaque élément
[1,2,3,4,5] → [f(1),f(2),f(3),f(4),f(5)]
Implémentation avec foldr
Accumulateur ← liste cible vide
Pour chaque élément e :
- Calculer f(e)
- Ajouter f(e) à la liste cible
fonction
composée
25. USAGES
Fonction map
List<Integer> accu = new ArrayList<>();
BiFunction<List<Integer>,Integer,List<Integer>> op =
(l,e) -> {
l.add(0, e * 2);
return l;
};
List<Int> result = foldr(op, accu, nums);
26. USAGES
Fonction filter
Sélectionne uniquement les éléments qui
répondent à un prédicat p
[1,2,3,4,5] –-(garder si >3)--> [4,5]
Implémentation avec foldr
Accumulateur ← liste cible vide
Pour chaque élément e :
- Vérifier p(e)
- Si p(e), ajouter e à la liste cible
fonction
composée
27. USAGES
Fonction filter
ajout de l'élément à
l'accumulateur
let op e list = if e
then
else
> 3
(e:list)
list
result = foldr op [] [1..5]
28. USAGES
Fonction filter
List<Integer> accu = new LinkedList<>();
BiFunction<List<Integer>,Integer,List<Integer>> op =
(l, e) -> {
if (e > 3) l.add(0,e);
return l;
};
List<Int> result = foldr(op, accu, nums);
29. USAGES
Fonction count
Compte le nombre d'éléments dans la liste
[1,2,3,4,5] → 5
Implémentation avec foldl
Accumulateur ← 0
Pour chaque élément e :
- Incrémenter l'accumulateur
31. USAGES
Fonction max
Renvoie le plus grand élément de la liste
[1,2,3,4,5] → 5
Implémentation avec foldl
Accumulateur ← 0
Pour chaque élément e :
- Si e > accumulateur, alors accumulateur = e
32. USAGES
Fonction max
let op e m = if e
then
else
> m
e
m
result = foldl op 0 [1..5]
Integer max = foldl((e,m)-> e>m?e:m, 0, nums);
33. IMPLEMENTATIONS
Java 8
Notion de Stream (java.util.stream.Stream)
Pipeline de transformation
Spécialisé par type (IntStream…)
Implémente reduce (foldl restreint à 1 type)
T reduce( T identity,
BinaryOperator<T> reducer );
List<Integer> nums = Arrays.asList(1,2,3,4,5);
int sum = nums.stream()
.reduce(0, Integer::sum);
34. IMPLEMENTATIONS
Javascript
Sur les tableaux
reduce (foldl) et reduceRight (foldr)
array.reduce
(func,
array.reduceRight(func,
initval)
initval)
var nums = new Array(1,2,3,4,5);
var sum = nums.reduce(
function(a,e) {return a+e;},
0);
35. IMPLEMENTATIONS
Scala
Sur les listes et tableaux
foldLeft (/:) et foldRight (:)
List.foldLeft (initval)(func)
List.foldRight(initval)(func)
val nums = Array(1, 2, 3, 4, 5)
val sum = nums.foldLeft(0)(_+_)
val sum = (0 /: list)(_+_)
37. CONCLUSION
Alors, What's The Fold ?
Un principe simple, puissant et générique
Socle pour d'autre opérations
map, filter, count...
Déjà présent dans vos langages
reduce, inject, foldLeft...
Apprenez à le reconnaître !