wtf - what's the fold - bordeaux jug 2013

38
WTF ? WTF ? (What's the Fold) Olivier Croisier principes de programmation fonctionnelle, expliqués simplement

Upload: zenika

Post on 16-Jun-2015

594 views

Category:

Technology


3 download

DESCRIPTION

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 !

TRANSCRIPT

Page 1: WTF - What's The Fold - Bordeaux JUG 2013

WTF ?WTF ?(What's the Fold)

Olivier Croisier

principes de programmation fonctionnelle, expliqués simplement

Page 2: WTF - What's The Fold - Bordeaux JUG 2013

[email protected]

TheCodersBreakfast.net

github.com/OlivierCroisier

@OlivierCroisier

Olivier Croisier

JavaSpecialist™Consultant @Zenika

FormateurSpeakerBlogger

SPEAKER

Page 3: WTF - What's The Fold - Bordeaux JUG 2013

Programmation fonctionnelle

Haskell, Scala... ?

Difficile ?

Vocabulaire Functor, Applicative, Monoid, Monad, Arrow... map(), filter()... fold()

FOLD ?

Page 4: WTF - What's The Fold - Bordeaux JUG 2013

Définition en Haskell

… Pas de panique !

foldl :: (a b a) a [b] a→ → → → →foldl :: (a b a) a [b] a→ → → → →

FOLD ?

Page 5: WTF - What's The Fold - Bordeaux JUG 2013

Exercice 1

Soit une liste d'entiers → Les additionner

List<Integer> nums = Arrays.asList(1,2,3,4,5);List<Integer> nums = Arrays.asList(1,2,3,4,5);

FOLD ?

Page 6: WTF - What's The Fold - Bordeaux JUG 2013

Exercice 1

Soit une liste d'entiers → Les additionner

public Integer sum(List<Integer> nums) { Integer sum = 0; for (Integer num : nums) { sum = sum + num; } return sum;}

public Integer sum(List<Integer> nums) { Integer sum = 0; for (Integer num : nums) { sum = sum + num; } return sum;}

FOLD ?

List<Integer> nums = Arrays.asList(1,2,3,4,5);List<Integer> nums = Arrays.asList(1,2,3,4,5);

Page 7: WTF - What's The Fold - Bordeaux JUG 2013

Exercice 2

Soit une liste d'entiers → Les multiplier

List<Integer> nums = Arrays.asList(1,2,3,4,5);List<Integer> nums = Arrays.asList(1,2,3,4,5);

FOLD ?

Page 8: WTF - What's The Fold - Bordeaux JUG 2013

public Integer product(List<Integer> nums) { Integer product = 1; for (Integer num : nums) { product = product * num; } return product;}

public Integer product(List<Integer> nums) { Integer product = 1; for (Integer num : nums) { product = product * num; } return product;}

FOLD ?

Exercice 2

Soit une liste d'entiers → Les multiplier

List<Integer> nums = Arrays.asList(1,2,3,4,5);List<Integer> nums = Arrays.asList(1,2,3,4,5);

Page 9: WTF - What's The Fold - Bordeaux JUG 2013

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;}

public Integer foo(List<Integer> nums) { Integer accu = <init>; for (Integer num : nums) { accu = accu <op> num; } return accu;}

FOLD ?

Page 10: WTF - What's The Fold - Bordeaux JUG 2013

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;

}

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;

}

Type de l'accumulateur

Type des éléments de la liste

FOLD ?

Page 11: WTF - What's The Fold - Bordeaux JUG 2013

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);

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);

FOLD ?

Page 12: WTF - What's The Fold - Bordeaux JUG 2013

BiFunction<Int,Int,Int> plus = (a,e) -> a+e;BiFunction<Int,Int,Int> mult = (a,e) -> a*e;

Integer sum = fold (plus, 0, nums);Integer product = fold (mult, 1, nums);

BiFunction<Int,Int,Int> plus = (a,e) -> a+e;BiFunction<Int,Int,Int> mult = (a,e) -> a*e;

Integer sum = fold (plus, 0, nums);Integer product = fold (mult, 1, nums);

Java 8 : Expressions Lambda

(args) -> expression

Integer sum = fold ((a,e)->a+e, 0, nums);Integer product = fold ((a,e)->a*e, 1, nums);Integer sum = fold ((a,e)->a+e, 0, nums);Integer product = fold ((a,e)->a*e, 1, nums);

FOLD ?

Page 13: WTF - What's The Fold - Bordeaux JUG 2013

public class MathUtil { public static Integer mult(Integer x,Integer y) {

return x * y; }}

Integer prod = fold (MathUtil::mult, 1, nums);

public class MathUtil { public static Integer mult(Integer x,Integer y) {

return x * y; }}

Integer prod = fold (MathUtil::mult, 1, nums);

Java 8 : Références de méthodes

Class::staticFunction

Integer sum = fold (Integer::plus, 0, nums);Integer sum = fold (Integer::plus, 0, nums);

FOLD ?

Page 14: WTF - What's The Fold - Bordeaux JUG 2013

Définition en Haskell

foldl :: (a b a) a [b] a→ → → → →foldl :: (a b a) a [b] a→ → → → →

fonction (a,b) a→

accumulateurde type a

liste<b>

résultat

FOLD ?

Page 15: WTF - What's The Fold - Bordeaux JUG 2013

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

Définition

PRINCIPES

Page 16: WTF - What's The Fold - Bordeaux JUG 2013

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

PRINCIPES

Page 17: WTF - What's The Fold - Bordeaux JUG 2013

Fold left - foldl

0 + 1 [1,2,3,4,5]

1 + 2 [1,2,3,4,5]

3 + 3 [1,2,3,4,5]

6 + 4 [1,2,3,4,5]

10 + 5 [1,2,3,4,5]

15

0 + 1 [1,2,3,4,5]

1 + 2 [1,2,3,4,5]

3 + 3 [1,2,3,4,5]

6 + 4 [1,2,3,4,5]

10 + 5 [1,2,3,4,5]

15

accumulateur

PRINCIPES

Page 18: WTF - What's The Fold - Bordeaux JUG 2013

Fold right - foldr

[1,2,3,4,5] 5 + 0

[1,2,3,4,5] 4 + 5

[1,2,3,4,5] 3 + 9

[1,2,3,4,5] 2 + 12

[1,2,3,4,5] 1 + 14

15

[1,2,3,4,5] 5 + 0

[1,2,3,4,5] 4 + 5

[1,2,3,4,5] 3 + 9

[1,2,3,4,5] 2 + 12

[1,2,3,4,5] 1 + 14

15

accumulateuritérationinversée !

PRINCIPES

Page 19: WTF - What's The Fold - Bordeaux JUG 2013

Fold left vs Fold right

Opérations non commutatives (soustraction, division...)

Performances Plus efficace d'ajouter en tête des listes → foldr : élément à gauche, liste à droite

foldl (-) 0 [1..5]foldr (-) 0 [1..5]foldl (-) 0 [1..5]foldr (-) 0 [1..5]

quiz !

PRINCIPES

Page 20: WTF - What's The Fold - Bordeaux JUG 2013

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]foldl (+) 0 [1..5]foldl1 (+) X [1..5]

valeur initiale implicite

PRINCIPES

Page 21: WTF - What's The Fold - Bordeaux JUG 2013

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]

scanl (+) 0 [1..5][0,1,3,6,10,15]

scanr (+) 0 [1..5][15,14,12,9,5,0]

PRINCIPES

Page 22: WTF - What's The Fold - Bordeaux JUG 2013

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)↔ ⋅f(g(x)) (f g)(x)↔ ⋅

USAGES

Page 23: WTF - What's The Fold - Bordeaux JUG 2013

Fonction map

Applique une fonction f à chaque élément

Implémentation avec foldr Accumulateur ← liste cible vide Pour chaque élément e : - Calculer f(e) - Ajouter f(e) à la liste cible

[1,2,3,4,5] [→ f(1),f(2),f(3),f(4),f(5)][1,2,3,4,5] [→ f(1),f(2),f(3),f(4),f(5)]

fonctioncomposée

USAGES

Page 24: WTF - What's The Fold - Bordeaux JUG 2013

Fonction map

result = foldr ((:).(*2)) [] [1..5]result = foldr ((:).(*2)) [] [1..5]

fonction f

composition de fonctions- application de f- puis ajout à la liste

"cons"

USAGES

Page 25: WTF - What's The Fold - Bordeaux JUG 2013

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);

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);

USAGES

Page 26: WTF - What's The Fold - Bordeaux JUG 2013

Fonction filter

Sélectionne uniquement les éléments qui répondent à un prédicat p

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

[1,2,3,4,5] -(garder si – >3)--> [4,5][1,2,3,4,5] -(garder si – >3)--> [4,5]

USAGES

fonctioncomposée

Page 27: WTF - What's The Fold - Bordeaux JUG 2013

Fonction filter

let op e list = if e > 3 then (e:list) else list

result = foldr op [] [1..5]

let op e list = if e > 3 then (e:list) else list

result = foldr op [] [1..5]

ajout de l'élément à l'accumulateur

USAGES

Page 28: WTF - What's The Fold - Bordeaux JUG 2013

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);

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);

USAGES

Page 29: WTF - What's The Fold - Bordeaux JUG 2013

Fonction count

Compte le nombre d'éléments dans la liste

Implémentation avec foldl Accumulateur ← 0 Pour chaque élément e : - Incrémenter l'accumulateur

[1,2,3,4,5] 5→[1,2,3,4,5] 5→

USAGES

Page 30: WTF - What's The Fold - Bordeaux JUG 2013

Fonction count

count = foldl (\a e a+1) 0 [1..5]→count = foldl (\a e a+1) 0 [1..5]→

Integer result = foldl((a,e)->a+1, 0, nums)Integer result = foldl((a,e)->a+1, 0, nums)

USAGES

Page 31: WTF - What's The Fold - Bordeaux JUG 2013

Fonction max

Renvoie le plus grand élément de la liste

Implémentation avec foldl Accumulateur ← 0 Pour chaque élément e : - Si e > accumulateur, alors accumulateur = e

[1,2,3,4,5] 5→[1,2,3,4,5] 5→

USAGES

Page 32: WTF - What's The Fold - Bordeaux JUG 2013

Fonction max

let op e m = if e > m then e else m

result = foldl op 0 [1..5]

let op e m = if e > m then e else m

result = foldl op 0 [1..5]

Integer max = foldl((e,m)-> e>m?e:m, 0, nums);Integer max = foldl((e,m)-> e>m?e:m, 0, nums);

USAGES

Page 33: WTF - What's The Fold - Bordeaux JUG 2013

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 );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);

List<Integer> nums = Arrays.asList(1,2,3,4,5);

int sum = nums.stream() .reduce(0, Integer::sum);

IMPLEMENTATIONS

Page 34: WTF - What's The Fold - Bordeaux JUG 2013

Javascript

Sur les tableaux reduce (foldl) et reduceRight (foldr)

array.reduce (func, initval) array.reduceRight(func, initval)array.reduce (func, initval) array.reduceRight(func, initval)

var nums = new Array(1,2,3,4,5);

var sum = nums.reduce( function(a,e) {return a+e;}, 0);

var nums = new Array(1,2,3,4,5);

var sum = nums.reduce( function(a,e) {return a+e;}, 0);

IMPLEMENTATIONS

Page 35: WTF - What's The Fold - Bordeaux JUG 2013

Scala

Sur les listes et tableaux foldLeft (/:) et foldRight (:\)

List.foldLeft (initval)(func) List.foldRight(initval)(func)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)(_+_)

val nums = Array(1, 2, 3, 4, 5)

val sum = nums.foldLeft(0)(_+_)val sum = (0 /: list)(_+_)

IMPLEMENTATIONS

Page 36: WTF - What's The Fold - Bordeaux JUG 2013

Groovy

Sur les listes list.inject (foldl)

nums = [1,2,3,4];

sum = nums.inject(0) { a,e -> a+e }

nums = [1,2,3,4];

sum = nums.inject(0) { a,e -> a+e }

IMPLEMENTATIONS

Page 37: WTF - What's The Fold - Bordeaux JUG 2013

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 !

CONCLUSION

Page 38: WTF - What's The Fold - Bordeaux JUG 2013

questions ?questions ?