1.
Expliquer ce que fait cette fonction, plus précisément en détaillant chacun des opérations qui sont invoquées
public static double mysteriousF(BasicStream<Integer> s) {
Pair<Integer, Integer> r = s.filter(x -> x % 2 == 0)
.map(x -> new Pair<>(1, x))
.reduce((p, q) -> new Pair<>(p.key() + q.key(), p.value() + q.value()))
.orElse(new Pair<>(1, 0));
return (double) r.value() / r.key();
}
Réponse
s.filter(x -> x % 2 == 0)
récupère les nombres pairs des
.map(x -> new Pair<>(1, x))
pour chaque nombre $x$, en fait une paire $(1, x)$.reduce((p, q) -> new Pair<>(p.key() + q.key(), p.value() + q.value()))
pour chaque couple de paires successivesp
etq
, remplace par une nouvelle paire contenant la somme de chaque membre dep
etq
- à la fin de ce parcours, on obtient une paire dont le premier membre contient la somme de tous les premiers membres des paires, et idem pour le second
- comme le premier membre commençait à 1, en fait le premier membre final contient juste le nombre d'éléments
- le second membre commence avec la valeur de chaque nombre pair, le second membre final contient la somme de tous ces nombres
.orElse(new Pair<>(1, 0))
s'il n'y avait aucun nombre pair, on utilise $(1, 0)$ comme valeur par défaut- au point où on en est,
r
contient le nombre de nombre pairs, et leur somme (double) r.value() / r.key()
donne donc la moyenne (arithmétique) de tous les nombres pairs des
2.
Dans la classe SupplierStream, voici la fonction filter proposée comme correction (rappel: s est l'attribut déclaré dans SupplierStream de la manière suivante:
final Supplier<Optional<T>> s;
Expliquer pourquoi la fonction filter contient une boucle while (alors que filter sur un stream n'est pas considéré comme une opération terminale, mais, au contraire comme une opération intermédiaire)
public BasicStream<T> filter(Predicate<T> predicate) {
return new SupplierStream<>( () -> {
Optional<T> o = s.get();
while (o.isPresent() && !predicate.test(o.get())) {
o = s.get();
}
return o;
});
}
Réponse
Le but de filter
est de... filtrer un stream selon un prédicat. Donc à chaque
itération on donne le prochain élément validant le critère.
Autrement dit, à chaque fois qu'on demande au stream résultat son prochain élément, il faut parcourir le stream entrant jusqu'à trouver un élément validant le critère.
Seulement là en peut renvoyer cet élément.
Le while
dans la fonction filter
modélise cet aspect de devoir chercher
dans le stream d'entrée le prochain élément validant le critère.
3.
Etant donné la classe ValueHolder
static class ValueHolder<T> {
private T inner;
ValueHolder(T value) {
this.inner = value;
}
T set(T value) {
T old = inner;
inner = value;
return old;
}
T get() {
return inner;
}
}
expliquer ce que fait cette fonction mysteriousF2, et au passage, expliquer pourquoi la fonction renvoie un Supplier
public static <T> Supplier<T> mysteriousF2(Lst<T> l) {
ValueHolder<Lst<T>> cell = new ValueHolder<>(l);
return () -> cell.get() == null ? null : cell.set(cell.get().cdr()).car();
}
Ensuite, donner une fonction qui permet de tester mysteriousF2 étant donné cette liste de String:
Lst<String> l = new Lst<>("pomme", new Lst<>("raisin", new Lst<>("poire", null)));
Supplier<String> monsupplier = mysteriousF2(l);
- Comment déclencher la fonction reférencée par monsupplier
- Que se passe-t-il si cette fonction référencée par monsupplier est invoquée deux fois: expliquer quelle sera la valeur renvoyée de type String à l'issue de chacune de ces deux invocations? Expliquer ce qu'il se passera si on invoque monsupplier 100 fois ?
Réponse
La classe ValueHolder
sert de conteneur pour une valeur. En Java,
une closure ne peut pas modifier la valeur d'une variable capturée. Cependant,
modifier la valeur d'un membre d'un objet ne "compte pas" comme une modification.
Mettre une valeur à l'intérieur d'un objet (ici ValueHolder
) permet de contourner
cette règle et de permettre à la closure de modifier une valeur extérieure.
Ici, mysteriousF2
prend initialement en entrée une liste liée, puis elle renvoie une fonction.
Cette fonction est de type Supplier
puisqu'elle ne prend pas de paramètre et renvoie une valeur.
En particulier, c'est une closure car elle fait référence à cell
, variable définie à l'extérieur.
Cette closure n'est pas safe car elle affecte mutablement la valeur d'une variable extérieure, ce qui fait qu'elle n'est en outre pas pure car son comportement dépend de la valeur de cell
.
En pratique, cell
contient initialement (dans le ValueHolder
) la liste passée, puis à chaque appel, le car
de la valeur de cell
est renvoyé, puis cell
est modifiée pour contenir le cdr
.
Concrètement, mysteriousF2
renvoie un itérateur sur la liste passée ; c'est-à-dire une closure qui à chaque appel renvoie l'élément suivant dans la liste, puis renvoie null
indéfiniment quand on est rendu à la fin de la liste.
Voici un exemple de test pour mysteriousF2
. On utilise .get()
pour déclencher la fonction référencée par monsupplier
(cf la définition de l'interface Supplier<T>
).
String current;
while ((current = monsupplier.get()) != null) {
System.out.println(current);
}
System.out.println("Liste terminée");
Comme on l'a dit, la fonction itère dans la liste. Donc si on invoque la fonction deux fois, on aura
successivement le premier ("raisin"
) puis le deuxième ("poire"
) élément de la liste. Si on l'invoque 100 fois, on
aura les trois premiers éléments de la liste, puis les 97 appels suivants renverront null
car on est arrivé à la fin de la liste.
4.
Etant donné l'implémentation de limit fournie en correction pour SupplierStream
public BasicStream<T> limit(long maxSize) {
AtomicInteger c = new AtomicInteger(0);
return new SupplierStream<>(() ->
s.get().filter(x -> c.getAndIncrement() < maxSize));
}
Etant donné que la fonction filter sur un Optional est définie ainsi (voir ci dessous), réécrire la fonction limit sans utiliser cette fonction filter:
Optional<T> filter(Predicate<? super T> predicate)
If a value is present, and the value matches the given predicate, returns an Optional describing the value, otherwise returns an empty Optional.
Parameters:
predicate the predicate to apply to a value, if present
Returns:
an Optional describing the value of this Optional, if a value is present and the value matches the given predicate, otherwise an empty Optional
Réponse
public BasicStream<T> limit(long maxSize) {
AtomicInteger c = new AtomicInteger(0);
return new SupplierStream<>(() -> {
if (c.getAndIncrement() < maxSize) {
return s.get();
} else {
return Optional.empty();
}
});
}