• Aucun résultat trouvé

Pour aller plus loin : Lambda expressions, Streams et Optional

N/A
N/A
Protected

Academic year: 2022

Partager "Pour aller plus loin : Lambda expressions, Streams et Optional"

Copied!
8
0
0

Texte intégral

(1)

Pour aller plus loin : Lambda expressions, Streams et Optional

Denis Conan

CSC4102

(2)

Table des matières

Pour aller plus loin : Lambda expressions, Streams et Optional Denis Conan, , Télécom SudParis, CSC4102

Janvier 2022 1

Sommaire 3

1 Pourquoi des lambda expressions JAVA ?* 4

2 Différents types de références de méthodes 6

3 Pipeline et stream 7

3.1 Début du pipeline . . . . 7

3.2 Traitements intermédiaires du pipeline . . . . 7

3.3 Terminaison d’un pipeline . . . . 8

(3)

Optional

# 2

'

&

$

%

Sommaire

1 Pourquoi des lambda expressions JAVA ?* . . . 6 2 Différents types de références de méthodes . . . 7 3 Pipeline et stream . . . 10

Dans les sections qui suivent, nous complétons les diapositives du cours avec quelques détails supplé-

mentaires. Ces informations supplémentaires sont importantes pour aller plus loin que les premiers éléments

donnés en cours ; elles sont donc à étudier après la compréhension des diapositives du cours ainsi que la

réalisation du TP correspondant.

(4)

# 3

'

&

$

%

1 Pourquoi des lambda expressions JAVA ?*

Voici un scénario extrait du livre de M. Naftalin, «Mastering Lambdas : Java Programming in a Multicore World», Mc Graw Hill, Oracle Press, 2015, pour expliquer l’intérêt deslambda expressions

1. Commençons avec un code qui itère sur une collection d’objets modifiables

List<Point> pointList = Arrays.asList(new Point(1,2), new Point(2,4));

for(Point p : pointList) { p.translate(1,1); }

La seconde ligne de ce code est traduite par le compilateur comme suit :

Iterator pointItr = pointList.iterator();

while (pointItr.hasNext()) { ((Point) pointItr.next()).translate(1,1); }

Problème : le compilateur génère du code qui sera toujours séquentiel

Nous souhaiterions une exécution qui puisse être parallèle à la demande, c’est-à-dire selon la mise en œuvre de la méthode forEach (qui suit)

D’où, nous souhaitons quelque chose comme ceci :

List<Point> pointList = Arrays.asList(new Point(1,2), new Point(2,4));

pointList.forEach(/*translation d’un point selon le vecteur (1,1)*/);

# 4

'

&

$

% 2. Solution

(avec le patron de conception « Commande »)

// cette interface est définie dans java.lang (présentation simplifiée ici) public interface Iterable<T> { ...

default void forEach(Consumer<? super T> action) { for (T t : this) { action.accept(t); }

}}

// cette interface est définie dans java.util.function

public interface Consumer<T> { void accept(T t); }

Avec la nouvelle interface Consumer<T>, le code précédent devient :

List<Point> pointList = Arrays.asList(new Point(1,2), new Point(2,4));

pointList.forEach(new Consumer<Point>() {

public void accept(Point p) { p.translate(1,1); } });

(5)

Optional

# 5

'

&

$

%

■ Une curiosité = méthode default dans une interface

♦ https://docs.oracle.com/javase/tutorial/java/IandI/

defaultmethods.html

■ Une nouveauté : Consumer<T> est une interface qui possède une seule méthode : void accept(T t)

■ Une difficulté de lecture = classe anonyme

♦ https://docs.oracle.com/javase/tutorial/java/javaOO/

anonymousclasses.html

# 6

'

&

$

% 3. Remplacement de la classe anonyme par une lambda expression

(a) Le compilateur devrait pouvoir inférer l’interface attendue par forEach

pointList.forEach(new Consumer<Point>() {

public void accept(Point p) { p.translate(1,1); } });

(b) Avec le nom de l’interface attendue, le compilateur devrait pouvoir inférer le nom de la méthode

pointList.forEach( Point

public void accept(Point p) { p.translate(1,1); } );

(c) Le type de l’argument peut alors être inféré du type appelant (pointList)

pointList.forEach( Point

Point p p.translate(1,1) );

(d) Avec une syntaxe particulière (->), cela donne une lambda expression

pointList.forEach(p -> p.translate(1, 1));

(6)

# 7

'

&

$

%

2 Différents types de références de méthodes

Forme

«lambda expression»

Forme

« référence de méthode »

Type de méthode de référence 1 str -> Integer.parseInt(str) Integer::parseInt Statique 2 Instant then =

Instant.now();

t -> then.isAfter(t)

Instant.now()::isAfter Lié (bound)

3 str -> str.toLowerCase() String::toLowerCase Libre (unbound) 4 () -> new TreeMap<K,V> TreeMap<K,V>::new Constructeur

de classe

5 len -> int[len] int[]::new Constructeur

de tableau

• Les références de méthodes sont encore plus succinctes que les lamddas

Des outils comme SpotBugs

1

et SonarLint

2

dans Eclipse proposent systématiquement les rem- placements

Lors du remplacement de la lambda par la référence, attention aux références de méthodes avec variables liées (#2) : variable « then » définie avant la définition de la lambda expression Versus méthode « Instant.now() » appelée dans la définition de la référence de méthode (cf. un exemple dans la diapositive qui suit)

• Exemples de références de méthodes

Classe seance8/LambdaVersusReferenceDeMethode

1 T o I n t F u n c t i o n < String > f1L = str - > I n t e g e r . p a r s e I n t ( str ) ;

2 T o I n t F u n c t i o n < String > f 1 R M = I n t e g e r :: p a r s e I n t ;

3 // e s s a i de f1L et f 1 R M

4 S y s t e m . out . p r i n t l n ( f1L . a p p l y A s I n t ( " 1 " ) + " ␣ " + f 1 R M . a p p l y A s I n t ( " 1 " ) ) ;

5 I n s t a n t t h e n = I n s t a n t . now () ;

6 P r e d i c a t e < Instant > f2L = t - > t h e n . i s A f t e r ( t ) ;

7 P r e d i c a t e < Instant > f 2 R M = I n s t a n t . now () :: i s A f t e r ;

8 // e s s a i de f2L et f 2 R M + i d e m en l a m b d a s

9 S y s t e m . out . p r i n t l n ( f2L . t e s t ( t h e n ) + " ␣ " + f 2 R M . t e s t ( t h e n ) ) ;

10 S y s t e m . out . p r i n t l n ( t h e n . i s A f t e r ( t h e n ) + " ␣ " + I n s t a n t . now () . i s A f t e r ( t h e n ) ) ;

11 U n a r y O p e r a t o r < String > f3L = str - > str . t o L o w e r C a s e () ;

12 U n a r y O p e r a t o r < String > f 3 R M = S t r i n g :: t o L o w e r C a s e ;

13 // e s s a i f3L et f 3 R M

14 S y s t e m . out . p r i n t l n ( f3L . a p p l y ( " ABC " ) + " ␣ " + f 3 R M . a p p l y ( " ABC " ) ) ;

15 S u p p l i e r < TreeMap < String , String > > f4L = () - > n e w TreeMap < String , String >() ;

16 S u p p l i e r < TreeMap < String , String > > f 4 R M = T r e e M a p ::n e w;

(7)

Optional

# 8

'

&

$

%

3 Pipeline et stream

3.2 Début du pipeline . . . 10 3.2 Traitements intermédiaires du pipeline . . . 10 3.3 Terminaison d’un pipeline . . . 11

# 9

'

&

$

%

3.1 Début du pipeline

■ IntStream, etc. pour éviter les opérations de conversion entre le type conteneur Integer et le type primitif int

+ méthodes generate, iterate, range, of, etc. pour démarrer le pipeline

♦ P.ex.,

IntStream.iterate(1, i -> i * 2)

,

IntStream.range(7, 42)

■ Interface Collection maintenant avec les méthodes stream et parallelStream qui créent un stream

♦ P.ex.,

maListe.stream()

♦ Dans CSC4102, nous n’utiliserons pas de parallélisation

■ Plus rarement, classe Stream avec les méthodes de classe of, empty, etc.

♦ P.ex.,

Stream.of(new Point(1, 2), new Point(2, 4))

(8)

# 10

'

&

$

%

3.2 Traitements intermédiaires du pipeline

■ Transformation = retourne un stream d’un autre type d’éléments

Stream<R> map(Function<T,R> mapper)

et flatMap

DoubleStream mapToDouble(ToDoubleFunction<T> mapper)

, etc.

■ Filtrage =

Stream<T> filter(Predicate<T> predicate)

,

dropWhile

■ Combinaison de streams : concat

■ Tri (

sorted

), suppression des doublons (

distinct

), troncature (

limit

et

skip

)

■ Introspection pour le déverminage = même séquence en sortie + instertion d’un traitement avec

peek

IntStream.iterate(1, i -> i*2).limit(10).peek(System.out::println).anyMatch(v -> v==128);

# 11

'

&

$

%

3.3 Terminaison d’un pipeline

■ Recherche d’un élément et test de présence :

Optional<T> findAnya

,

findFirst

boolean anyMatch(Predicate<T>)

,

allMatch

, et

noneMatch

■ Réduction :

♦ Calcul :

int sum()

,

Optional<T> min()

,

Optional<T> max()

,

long count()

,

Optional<T> average()

,

IntSummaryStatistics summaryStatistics

♦ Collecte :

R collect(Collector<T,A,R)

avec T le type des valeurs du stream, R le type de la valeur de retour, et A le type intermédiaire lors d’une exécution en parallèle

▶ Bibliothèque d’algorithmes de

Collectors

:

toSet

,

toList

,

toMap

,

toCollection

joining

,

groupingBy

,

partitioningBy

■ Application d’un effet de bord :

void forEach(Consumer<T>)

,

forEachOrdered a. Cf. section sur la classeOptionaldes diapositives

Références

Documents relatifs

Dans la suite nous définirons les termes du lambda-calcul atomique et ses règles de réduction et nous en établirons les propriétés de base: systèmes de type simple, traduction

• sépare les données modifiables (références, tableaux, chaînes de caractères, enregistrements) dans des monades. • les monades peuvent utiliser tous les termes, mais seuls les

Barendregt, Henk; Dezani, Mariangiola, Lambda calculi with Types, 2010.... Models

• The normal reduction (each step contracts the leftmost-outermost redex) is a standard reduction. • Theorem [standardization] (Curry) Any reduction can

• A redex is created by reduction ρ if it is not a residual by ρ of a redex in initial term.. Residuals

Delia KESNER IRIF, CNRS et Universit ´e Paris kesner@irif.fr www.irif.fr/˜kesner.. Typed

Only the outermost redexes are reduced: a redex is reduced only when its right hand side has reduced to a value (variable or

Only the outermost redexes are reduced: a redex is reduced only when its right hand side has reduced to a value (variable or