A méditer: La solution la plus simple est toujours la moins compliquée
Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
Compiler Gcc sous windows avec MinGW
Coder efficacement en C++ : dans les bacs le 17 février 2014
mon tout nouveau blog
Ok.
Et les conteneurs utilisent bien des paires clé/valeur.
Bon, ben merci pour ces précisions.
Au fait, une idée folle m'a traversé l'esprit...
(Ceci dit, maintenant elle refuse de s'en aller... )
Je pensais utiliser un std::set plutôt qu'un tableau associatif, et utiliser un foncteur de comparaison comme celui-là :
Après tout, ce n'est pas parce que deux instances sont équivalentes qu'elles sont identiques...
Code Exemple pour NumericalConstant : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56 struct Compare { typedef std::unique_ptr<NumericalConstant const> ptr_type; // Pour le placement dans la collection bool operator () (ptr_type const& x, ptr_type const& y) const { return (x->getIntValue() < y->getIntValue()); } // Pour le test d'appartenance bool operator () (ptr_type const& x, int y) const { return (x->getIntValue() < y); } bool operator () (int x, ptr_type const& y) const { return (x < y->getIntValue()); } }; // struct Compare class NumericalConstantList : private std::set<std::unique_ptr<NumericalConstant const>, Compare> { typedef std::set<std::unique_ptr<NumericalConstant const>, Compare> _base; (...) public: const_iterator find(int value) const { const_iterator pos = begin(); while ((pos != end()) && key_comp()(*pos, value)) ++pos; if ((pos != end()) && !key_comp()(value, *pos)) return pos; return end(); } std::pair<const_iterator, bool> insert(value_type&& value) { const_iterator pos = begin(); while ((pos != end()) && key_comp()(*pos, value)) ++pos; if ((pos != end()) && !key_comp()(value, *pos)) return std::make_pair(pos, false); return _base::insert(pos, std::move(value)); } };
Bon, ça oblige à appeler find avant insert, mais c'est une fabrique qui s'occupera de ça...
Et puis à la réflexion, ce serait pareil avec un std::map.
Alors ?
Mauvaise idée, ou pourquoi pas ?
PS: Le List de NumericalConstantList n'a rien à voir avec la structure de donnée « liste ».
Je l'ai employé ici au sens plus... euh... disons « littéraire »...
Enfin disons qu'il veut dire : « C'est ici que sont stockées les constantes numériques, et pas ailleurs ».
En passant, c'est un singleton.
Si vous avez un terme plus approprié, je suis preneur...
(Cela fait je ne sais plus combien de temps que je cherche... )
PS 2: Je sais que std::set::iterator == std::set::const_iterator.
Bon, je crois que je vais prendre cette absence de réponse comme un « Mauvaise idée... » non assumé...
En fait, j'étais parti sur une réponse dans laquelle je m'emmelais les pinceaux et que je n'arrivais pas à rendre intelligible... j'ai donc préféré "dormir dessus" pour me donner une chance de remettre les idées en place
Déjà, il serait dommage (et donc, ce serait une mauvaise idée de el faire ) de devoir créer une instance d'un objet uniquement pour savoir s'il existe déjà, ne trouve tu pas
Ensuite, j'ai l'impression que ta hiérarchie de classe est très incomplète
Je verrais personnellement, au vu des classes existantes, au moins encore deux classes héritant de Term qui seront d'ailleurs sans doute encore spécialisées : Expression et Operator.
Operator permettrait de mettre deux termes en relation et Expression servirait de "point d'entrée" aux groupements de termes.
Le tout implémentant le DP "composite"
J'ai en effet l'impression (meme si je peux me tromper), au vu des noms que tu utilises pour tes classes, que tu essaye de mettre au point un arbre syntaxique ou lexical
Le fait est que, à ce moment là, tu devra faire la différence entre équivalence et égalité
L'équivalence est le fait que deux objets distincts aient les mêmes valeurs alors que l'égalité est le fait que deux pointeurs pointent exactement sur le même objet, la partie discriminante étant alors... l'adresse mémoire à laquelle l'objet se trouve
Mais, meme si la seule partie réellement discriminante est l'adresse mémoire à laquelle l'objet se trouve, tu te trouve face à une hiérarchie de classe ayant... sémantique d'objet, et non à une hiérarchie de classe ayant sémantique de valeur
En effet, lorsque tu en arriveras à coder tes opérateurs, ils nécessiteront un ou deux opérandes qui pourront au demeurant etre des expressions, ou des termes.
Si tu ne discrimines pas les opérandes que tu donnes à ces opérateurs par leur adresse, tu risque de te retrouver à faire des choses en boucle, et tu sera parti pour la galère
Maintenant, je vais peut etre beaucoup trop loin, parce que j'ai peut etre très mal interprété ton but... mais j'en serais surpris
A méditer: La solution la plus simple est toujours la moins compliquée
Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
Compiler Gcc sous windows avec MinGW
Coder efficacement en C++ : dans les bacs le 17 février 2014
mon tout nouveau blog
Je comprends.
Ça m'arrive aussi régulièrement...
Justement, la beauté de la chose, malgré le fait que j'utilise un std::set et non un std::map, c'est que je n'ai pas besoin de créer d'instance pour savoir si elle existe déjà.
C'est pour cette raison que le foncteur utilisé pour la comparaison des éléments possède trois opérateurs « () ».
D'ailleurs, la collection que j'utilise se comporte plus ou moins comme un map, même basée sur un set.
Lorsque l'on veut insérer une paire clé/valeur dans un map, il faut fournir les deux, et la valeur n'est réellement ajouté que si la clé n'est pas déjà présente.
Donc si on appelle directement « insert », on doit créer un objet, même si la même instance existe déjà.
Le seul moyen d'éviter ça est d'appeler d'abord « find » pour s'assurer que la clé n'est pas déjà présente avant de tenter d'insérer la valeur.
Avec ma collection, c'est exactement la même chose.
Si ce n'est que la clé n'est pas stockée avec la valeur...
C'est la vue d'une classe abstraite en bout de branche qui te fait dire ça ?
La classe « Expression » existe déjà.
Si si, regarde bien, là où il y a « Arithmetic » juste devant...
Effectivement elle se spécialise, en « UnaryExpression » et « BinaryExpression ».
Et il existe bien une hiérarchie basée sur une classe « Operator », mais à part.
Du coup, « ArithmeticExpression » a un champ operator de type Operator*.
Les opérandes sont des champs de la hiérarchie des expressions, de type Term*.
Ah oui, on pourrait faire comme ça, en effet.
Mais est-ce vraiment nécessaire ?
Pour l'instant, on se contente de créer un nouvel opérateur à chaque expression rencontrée, via quelque chose qui ressemble à une factory. (enum + switch)
Pas exactement, mais c'est vrai qu'on peut voir ça comme ça.
Comme je l'ai dit (je ne sais plus trop où... ), une fois les objets créés, on n'y accède plus que par leur adresse.
Et si l'on voulait ajouter des fonctionnalités à l'application, on passerait par les clés générées lors de la lecture du fichier d'entrée.
Donc pas de souci, maintenant je suis bien convaincu que mes classes ont une sémantique d'entité, et j'utilise les instances en conséquence.
Je ne peux pas lire dans tes pensées (ni toi dans les miennes, d'ailleurs... ), mais tes interventions me semblent pertinentes.
Donc si erreur d'interprétation il y a, elle ne doit pas être énorme...
Vous avez un bloqueur de publicités installé.
Le Club Developpez.com n'affiche que des publicités IT, discrètes et non intrusives.
Afin que nous puissions continuer à vous fournir gratuitement du contenu de qualité, merci de nous soutenir en désactivant votre bloqueur de publicités sur Developpez.com.
Partager