:: Enseignements :: ESIPE :: E4INFO :: 2012-2013 :: Java Avancé ::
![[LOGO]](http://igm.univ-mlv.fr/ens/resources/mlv.png) | foreach, filter, map, reduce |
Le but de ce TD est d'implanter les méthodes ditew fonctionnelles
que l'on trouve habituellement sur les collections de langage comme Python ou Ruby.
L'interface
FunIterable est un
Iterable
qui ajoute les méthodes
forEach,
toList et
filter.
Les méthodes de
FunIterable sont définies comme ceci:
-
La méthode forEach() appelle la méthode apply
d'un Block pour chaque élement.
-
La méthode toList() copie l'ensemble des élements
de l'Iterable dans la liste passée en paramètre.
-
La méthode filter() renvoie un FunIterable
qui filtre les élements du FunIterable courant.
Avec les interfaces
Block et
Filter définies comme ceci:
Exercice 1 - FunUtils
Le but de cet exercice est de founir une implantation de l'interface
FunIterable. Pour cela, la classe FunUtils contiendra
un ensemble de méthodes statiques aidant à l'implantation de l'interface
FunIterable.
-
On souhaite écrire une méthode filterIterator qui prend en paramètre un
Iterable et un filtre Filter et renvoie un
Iterator qui parcourt l'ensemble des éléments de l'iterable
pour lequel la méthode accept du filter renvoie vrai.
Expliquer pourquoi la méthode remove ne peut pas être implantée.
Implanter la méthode filterIterator.
-
Vérifier que le code suivant compile en écrivant un main:
Iterable<String> iterable = Arrays.asList("foo", "bar");
Iterator<String> iterator = FunUtils.filterIterator(iterable,
new Filter<Object>() {
public boolean accept(Object o) {
return o.toString().length() % 2 == 0;
}
});
Si le code ne compile pas, modifier la signature de la méthode filterIterator
pour qu'il compile.
-
Modifier le code du main pour que l'appel à filterIterator
renvoie un Iterator<CharSequence>.
Pourquoi le compilateur n'est pas capable de trouver tout seul E ?
-
De la même façon, les signatures des méthodes forEach, toList et
filter de l'interface FunIterable ne sont pas les bonnes,
modifier-les en conséquence.
-
Ecrire une méthode and qui prend deux Filter en paramètre
et qui renvoie un nouveau Filter dont la méthode accept
renverra vrai si les deux filtres sont vrais.
On cherche, de plus, à ce que l'implantation soit lazy comme le 'et' booleéen en C (ou en Java).
-
Ecrire une méthode trueFilter qui renvoie un filtre dont la méthode accept
renvoie toujours vrai.
Noter que ce filtre ne dépend pas du type d'élement passé en paramètre,
donc il est possible d'utiliser un même objet filtre que cela soit un Filter<String>
ou un Filter<Object>.
On stockera donc cet unique filtre comme une constante;
-
Ecrire une méthode asFunIterable qui prend un Iterable et un Filter
et qui renvoie un FunIterable dont les élements sont ceux de l'itérable filtrés par le Filter.
-
Modifier le code de filter de la classe implantant
FunIterable en remarquant qu'il est possible de détecter
si le FunIterable a été construit avec le filtre trueFilter,
et donc qu'il n'est alors pas nécessaire d'utiliser and.
-
Le test Junit est
FunUtilsTest.java.
Si certaines méthodes ne compilent pas, c'est que vous vous êtes trompé
dans la signature de la méthode.
Exercice 2 - FunUtil2
On cherche maintenant à écrire un
Itérateur qui associe
à un élement un autre élément en utilisant une fonction
définie par l'interface
Mapper.
-
Dans une classe FunUtils2, écrire une méthode
mapperIterator qui prend en paramètre un Iterable
et un Mapper et qui renvoie un Iterateur dont
chaque élement renvoyé est le résultat de l'appel à la méthode map
sur les élement de l'iterable passé en paramétre.
Que dire de la méthode remove ?
-
Ecrire une méthode compose qui prend deux Mapper en paramètre
qui renvoie un mapper qui agit comme une composition de fonction.
-
Ecrire une méthode identityMapper qui renvoie un mapper
qui renvoie le même objet que celui qu'on lui passe en paramètre.
Noter que ici aussi, il n'est pas nécessaire d'allouer l'objet
correspondant au mapper identité à chaque fois que l'on appelle
la méthode identityMapper.
-
Ajouter à l'interface FunIterable la méthode map
qui prend un Mapper en paramètre et qui renvoie un nouveau FunIterable
dont l'iterateur renvoie les images des élements du FunIterable courant
par la fonction de mapping.
Noter que la signature en commentaire dans l'interface n'est pas la bonne.
Quelle doit être la bonne signature?
-
Ecrire une méthode asFunIterable dans la classe FunUtils2
qui prend un iterable et un mapper en paramètre et renvoie un FunIterable.
La méthode map devra être optimisée pour éviter de créer des
FunIterable si cela n'est pas nécessaire.
Noter qu'il vous faudra aussi changer l'implantation dans FunUtils
car on vient de changer l'interface.
Exercice 3 - FunUtil3
On cherche enfin à avoir une implantation de FunIterable
qui permette de filtrer et mapper en une seule opération, i.e sans
créer des FunIterable de FunIterable.
-
Ecrire, dans la classe FunUtils3, une méthode mapFilterIterator
qui prend en paramètre un iterable, un filter puis un mapper et renvoie
un itérateur des éléments filtrés puis mappés.
Que peut-on dire sur la méthode remove de l'itérateur?
-
Ecrire la méthode asFunIterable qui prend en paramètre
un Iterable, un Filter et un Mapper et
qui renvoie une classe implantant FunIterable
capable d'appliquer un filtre puis un mapper en une seule opération.
Les méthodes filter et map devront être optimisées pour
ne créer des FunIterable de FunIterable que si nécessaire.
Exercice 4 - Reduce [A la maison]
On souhaite ajouter une méthode
reduce appelée aussi fold, lfold suivant
les langages qui permet d'obtenir une valeur en itérant sur un itérable,
voici par exemple la définition en Python
reduce.
En fait il existe deux sortes de reduce, le reduce homogéne qui pour
un iterable sur un type va fournir un résultat du même type en utilisant une fonction
qui, à deux élements de même type, fait correspondre un élement de ce même type et
le reduce hétérogène qui va retourner une valeur dont le type peut être différent
du type des objets de l'iterable et qui va, pour chaque élément, appeler une fonction
qui prendra l'ancienne valeur et l'élément courant pour retourner une nouvelle valeur.
-
Ecrire l'interface Reducer qui correspondra à un reduce homogène
et ajouter une méthode reduce à l'interface FunIterable.
Vous changerez les implantations en conséquence.
-
Ecrire l'interface Folder qui correspondra à un reduce hétérogène
et ajouter une méthode fold à l'interface FunIterable.
Vous changerez les implantations en conséquence.
-
Fournissez les tests Junit qui valident vos implantations.
© Université de Marne-la-Vallée