Une histoire d'additions !

Le problème exposé dans ce sujet a été résolu.

Bonjour à tous,

Je fais actuellement un TP de Java dans lequel j'ai plusieurs classes : 1. Une classe Agent, disposant notamment de l'attribut list_mission_objects (une ArrayList d'instances de MissionObject).

  1. Une classe étonnamment non-abstraite "MissionObject", disposant de l'attribut de type int poids.

  2. Des classes héritant de MissionObject : RadioactiveObject, PreciousObject…

Sujet du TP

Je bute sur la dernière question du TP. Il est demandé de calculer le poids total des :MissionObject d'un :Agent donné de trois façons différentes : 1. Avec un for ;

  1. Avec un iterator ;

  2. Avec une approche strictement fonctionnelle.

Mes solutions

Alors pour les 2 premiers points, c'est facile : 1. Je parcours mon ArrayList avec le for jusqu'à la taille de l'ArrayList, j'utilise la méthode ArrayList:get(int) pour récupérer le :MissionObject en cours et à chaque tour de boucle j'ajoute le poids (:MissionObject:getWeight) dans une variable poids déclarée juste avant le for.

  1. Pour l'itérateur, je le crée juste avant un while(my_iterator.hasNext()) en utilisant la méthode :ArrayList:iterator(). Puis dans ce while, j'ajoute le poids du :MissionObject courant (ite.next().getPoids()) dans une variable déclarée avant ce while.

Bref rien de bien sorcier.

Le problème que je rencontre

Par contre… Je ne vois pas du tout quoi faire pour l'approche strictement fonctionnelle : je ne pense pas qu'il faille utiliser la récursivité.

Et c'est là que je butte.... Quelqu'un aurait une idée ?

+0 -0

Nan mais je suis d'accord qu'utiliser de la récursion c'est pas vraiment une bonne idée. En passant par un stream, ça donnerait quelque chose comme ça (sur une simple liste d'entiers):

1
2
3
4
import java.util.*;

List<Integer> list = Arrays.asList(1,2,3,4);
Integer res = list.stream().reduce(0, Integer::sum); // res = 10

Je sais pas si c'est la bonne manière de faire, je suis plus trop à jour niveau Java.

+1 -0

Heum merci, alors pour l'instant je regarde du côté de la méthode Java 8 forEach.

1
2
 this.liste_objets_de_mission.forEach(k -> {int poids_func = 0; poids_func += k.getPoids(); });
 System.out.println("Poids porté par cet agent (POIDS_FONCTION) : " + poids_func);

Mais a priori je n'arrive pas à utiliser poids_func dans le System.out.println

Enfin de toute façon l'affectation à 0 dans le forEach empêche le calcul de bien se faire -_-'

+0 -0

C'est normal, si tu regardes le type que retourne forEach (void). Autrement dit, c'est équivalent à faire:

1
2
3
4
for (T elem : liste_objets_de_mission) {
    int poids_func = 0;
    poids_func += elem.getPoids();
}

forEach c'est pour modifier chaque élément (ou appliquer une action avec des effets de bords, i.e un affichage) d'une collection mais tu ne retournes rien.

+0 -0

Salut Lern-X,

Je ne sais jamais trop quand c'est considéré comme du "strictement fonctionnel" ou non mais l'usage des Stream de l'API des collections Java semble être une bonne première indication.

A partir de là, quelque chose comme ça devrait fonctionner :

1
int sum = agent.list_mission_objects.stream().map(MissionObject::getPoids).sum();

Salut !

Du temps où je faisais du Java, il me semble qu'il y avait des méthodes qui permettaient de définir le comportement des opérateurs quand ils rencontraient des objets du type dans lequel ces méthodes étaient implémentées (un peu comme toString qui est appelée automatiquement quand on a un affichage d'un objet). A l'époque, on avait créé une classe qui permettait des calculs en base 255, et on les additionnait, soustrayait, multipliait et divisait avec les opérateurs mathématiques usuels, mais la logique était directement dans la classe — évidemment, utiliser des types différents ne fonctionnait pas. Je n'ai plus le code sous la main, et je ne sais plus comment on faisait, ni si c'est encore possible (et le peu de recherche que je viens de faire me fiche un gros doute sur mes souvenirs), mais ça pourrait être une solution aussi.

+0 -0

Salut !

Du temps où je faisais du Java, il me semble qu'il y avait des méthodes qui permettaient de définir le comportement des opérateurs quand ils rencontraient des objets du type dans lequel ces méthodes étaient implémentées (un peu comme toString qui est appelée automatiquement quand on a un affichage d'un objet). A l'époque, on avait créé une classe qui permettait des calculs en base 255, et on les additionnait, soustrayait, multipliait et divisait avec les opérateurs mathématiques usuels, mais la logique était directement dans la classe — évidemment, utiliser des types différents ne fonctionnait pas. Je n'ai plus le code sous la main, et je ne sais plus comment on faisait, ni si c'est encore possible (et le peu de recherche que je viens de faire me fiche un gros doute sur mes souvenirs), mais ça pourrait être une solution aussi.

Ymox

Yo !

Oui tu parles de la surcharge d'opérateurs ! C'est heureusement toujours possible en Java, par contre je doute que ce soit utile d'en faire usage dans mon cas ! :)

Salut Lern-X,

Je ne sais jamais trop quand c'est considéré comme du "strictement fonctionnel" ou non mais l'usage des Stream de l'API des collections Java semble être une bonne première indication.

A partir de là, quelque chose comme ça devrait fonctionner :

1
int sum = agent.list_mission_objects.stream().map(MissionObject::getPoids).sum();

Andr0

Okay, je vais essayer ça et bidouiller si nécessaire, voir ce que ça donne. Merci !

Edit :

Ah zut, apparemment la méthode sum n'existerait pas sur ce que retourne le .map(...), c'est pas normal ça ?

Finalement en jetant un coup d'oeil à la JavaDoc (https://docs.oracle.com/javase/tutorial/collections/streams/reduction.html), j'ai trouvé ce code qui compile et qui fonctionne bien :

1
 double poids_func = this.liste_objets_de_mission.stream().mapToDouble(ObjetDeMission::getPoids).sum();

Sujet résolu ! Par contre je ne connais pas du tout "stream", "map", "mapToDouble", "reduce"… Quelqu'un peut me renseigner ? Je regarderai quelques tutos/la javaDoc aussi.

+0 -0

Par contre je ne connais pas du tout "stream", "map", "mapToDouble", "reduce"… Quelqu'un peut me renseigner ? Je regarderai quelques tutos/la javaDoc aussi.

C'est pas bien compliqué en fait.

stream() est une méthode apparue depuis Java 1.8 qui est le point d'entrée vers des méthodes apportant des notions de programmation fonctionnelle dans le langage. Grâce à cela, tu peux accéder aux méthodes filter(), map(), reduce() où les dérives comme mapToDouble() pour la méthode map() ou sum() pour la méthode reduce().

filter(), que tu n'utilises pas dans ton exercice parce que ce n'est pas nécessaire, permet de filtrer dans la liste. Par exemple, si tu voulais uniquement les poids d'une sous classe de MissionObject, il aurait été pertinent que tu utilises les filtres.

map(), mapToDouble() et les autres méthodes dans le genre permettent de transformer ta liste d'un type A à un type B. Dans ton exercice, tu transformes ta liste du type MissionObject vers une liste du type Double en appelant la méthode getPoids() sur la classe MissionObject.

Tu peux remarquer la syntaxe MissionObject::getPoids. C'est appelé des références de méthode. C'est simplement du sucre syntaxique pour éviter d'écrire sa version longue : (mission) -> mission.getPoids(). Les références de méthode peuvent être écrite si ta méthode n'a aucun paramètre.

reduce(), sum() et les autres méthodes dans le genre permettent de réduire sous une forme donnée ta liste. Dans ton exercice, nous réduisons ta liste en une valeur qui correspond à la somme des poids de ta liste.

Voilà, en espérant que tu comprendras avec ces explications.

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte