:: Enseignements :: Master :: M1 :: 2011-2012 :: Java Avancé ::
[LOGO]

foreach, filter, map, reduce


Le but de ce TD est d'implanter les méthodes dite fonctionnelles que l'on trouve habituellement sur les collections de language 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() appel 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éfinie comme ceci:

Exercice 1 - FunUtils

Le but de cette exercice est de founir une implantation de l'interface FunIterable pour cela, la classe FunUtils contiendra un ensemble de méthode statique aidant à l'implantation de l'interface FunIterable.

  1. On souhaite écrire une méthode filterIterator qui prend en paramètre un Iterable et un filtre Filter et renvoie un Iterator qui parcours l'ensemble des élements de l'iterable pour lequel la méthode accept du filter renvoie vrai.
    Expliquer pourquoi la méthode remove ne peut pas être implanté.
    Implanter la méthode filterIterator.
  2. 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 des deux filtres sont vrais.
    On cherche de plus à ce que l'implantation soit lazy comme le 'et' booleéen en C (ou en Java).
  3. Ecrire une méthode trueFilter qui renvoie un filtre dont la méthode accepte 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 cette unique filtre comme une constante;
  4. Ecrire une méthode asFunIterable qui renvoie un FunIterable à partir d'un Iterable. Ici, l'astuce consiste à utiliser la même classe en tant qu'implantation pour asFunIterable et asFunIterable(...).filter() car le FunIterable renvoyé par asFunIterable peut être vu comme un Iterable filtrant pour lequel le filtre est trueFilter.
    Notez de plus, que si un FunIterable est filtré par un trueFilter alors l'implantation de filter n'a pas besoin créer un FunIterable par dessus le FunIterable courant.
  5. Si certains tests Junit ne compile pas, c'est que la signature de vos méthodes n'est pas la bonne, il vous manque surement des wildcards. FunUtilsTest.java.

Exercice 2 - FunUtil2

On cherche maintenant à écrire un Iterateur qui associe à un élement un autre élement en utilisant une fonction définie par l'interface Mapper.

  1. 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 ?
  2. Ecrire une méthode compose qui prend deux Mapper en paramètre qui renvoie un mapper qui agit comme une composition de fonction.
  3. 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 appel la méthode identityMapper.
  4. Ajouter à l'interface FunIterable la méthode map qui prend en 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.
    Et fournissez une méthode asFunIterable à la classe FunUtils2 qui prend un iterable en paramètre et renvoie un FunIterable.
    La méthode map devra être optimisé 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
Le test Junit correspondant est FunUtils2Test.java.

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.

  1. Ecrire, dans la classe FunUtils3 une méthode mapFilterIterator qui prend en paramètre un iterable, un filter puis un mapper et renvoie un iterateur des élements filtrés puis mappés.
    Que peut-on dire sur la méthode remove de l'iterateur.
  2. Ecrire la méthode asFunIterable qui prend en paramètre un Iterable 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é pour ne créer des FunIterable de FunIterable que si nécessaire.
Le test Junit correspondant est FunUtils3Test.java.

Exercice 4 - Reduce [A la maison]

On souhaite ajouter une méthode reduce appelée aussi fold, lfold suivant les languages qui permet d'obtenir une valeur en iterant sur un iterable, 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 élement 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 élement appeler une fonction qui prendra l'ancienne valeur et l'élement courant pour retourner une nouvelle valeur.

  1. 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.
  2. 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.
  3. Fournissez les tests JUnit qui valident vos implantations.