IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage C++ Discussion :

Modele POO pour un QCM de calcul mental


Sujet :

Langage C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éclairé Avatar de Nono Sto
    Profil pro
    Inscrit en
    Mars 2009
    Messages
    350
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2009
    Messages : 350
    Par défaut Modele POO pour un QCM de calcul mental
    Chères amies, chers amis

    J'ai un peu de temps et je reprend un vieux projet.

    Je travaille sur le codage d'un QCM de calcul mental: cependant je séche sur le modèle OO.

    L'objectif est d'obtenir une suite de question de ce type:

    8 + 2 = ....,
    1/2 = .../4,
    ...+ 1/2 = 1

    Petite récap:

    Pour l'instant j'instancie une classe Exercice composé par un pointeur vers un objet probleme, un tableau de quatre réponse possible.

    L'objet probleme est constitué de deux opérandes.

    Maintenant je ne sais pas comment integrer les objet somme(+,-), et quotient(*,/), je subodore une relation d'heritage.

    Pourriez vous m'aider à la concetion du mon modele objet SVP.

    Merci

    Le code de la classe Probleme et Exercice:
    Code : 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
    #ifndef ProblemeH
    #define ProblemeH
     
    #include <ctime>
    #include <cmath>
    #include <string>
    #include <vector>
    #include <cstdlib>
    #include <iostream>
     
    #include "Fraction.h"
     
    template <class Type1, class Type2>
    class Probleme
     
    {
        private:        
            Type1 m_operande_1;
            Type2 m_operande_2;
     
        public :
            Probleme() : 
    				m_operande_1(), m_operande_2()
    			{
    			}
     
            Probleme(const Type1& operande_1, const Type2& operande_2) : 
    				m_operande_1(operande_1), m_operande_2(operande_2)
    			{
    			}
     
    		Probleme(Probleme const& probleme) : 
    				m_operande_1(probleme.m_operande_1), m_operande_2(probleme.m_operande_2)
    			{
    			}
     
    		virtual ~Probleme()
    			{
    			}
     
    		virtual void Afficher()const                     //défénition et declaration obligatoire des fonctions
    			{											 //virtuelle template dans le .hpp
    				Probleme<Type1,Type2> temp(*this);
    				std::cout<<temp.m_operande_1<<std::endl;
    			}
    		//virtual void Resultat() const;
    };
    #endif
    Code : 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
    #ifndef ExerciceH
    #define ExerciceH
     
    #include "Probleme.h"
     
    template <class Type1,class Type2>
    class Exercice
    {
        private :  
    		Probleme<Type1, Type2> *m_probleme;
            std::vector<Type1> m_proposition;
            std::vector<Type2> m_solution;
     
    		//Probleme<Type1, Type2> const& m_probleme; // passage par reference
     
        public :
    		Exercice(Probleme<Type1, Type2> *probleme): m_probleme(probleme), m_proposition(4), m_solution(4)
    			{
    				m_probleme = new Probleme<Type1, Type2>(*probleme);
    			}
     
    		/*Exercice(Probleme<Type1, Type2>  probleme): m_probleme (probleme), m_proposition(4), m_solution(4) 
    			{
    			}// constructeur pour le pasage par reference*/
     
            ~Exercice()
    			{
    				delete m_probleme;
    			}
    };
    #endif

  2. #2
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Salut,

    En fait, il faut faire la distinction entre le problème d'une part et l'expression qui le représente

    En effet, X + 1 = 4 est une expression, mais 3 * 6 + 12 / 2 = X est aussi une expression

    En outre, une expression de base peut etre composée de deux opérandes (1, 3, x, ) et d' un opérateur (+, - , *, / ), mais une expression peut, aussi, être composée de ... deux expression ( 3 * 6 et 1 2 / 2 ) et d'un opérateur ( + )

    L'idéal est donc de représenter une expression sous la forme d'un arbre binaire permettant de scinder l'expression sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
          =
         /  \
        +    4
       / \
      X   1
    ou encore de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
           =
          / \
        +     x
      /   \
     *      :
    / \    /\
    3  6  12 2
    En effet, de cette manière tu peux parfaitement faire en sorte que ton programme évalue lui-même l'inconnue ( x ) et qu'il "n'aura qu'à" comparer avec la valeur correspondant à la réponse de l'élève

    Pour créer ce genre d'arbre, c'est relativement facile, car on remarque que si tout est expression ( 3 peut etre considéré comme une expression ), les valeurs numériques (3, 6, x ) sont d'office des feuilles de l'arbre et les opérateurs sont d'office des branches (voir, des expressions)

    C'est une situation tellement habituelle qu'il existe même un desing pattern qui explique comment la mettre en oeuvre : le DP composite

    L'idée est donc de créer une classe de base (Expression), abstraite, qui présente une interface nous permettant de travailler avec les expressions et qui se présenterait sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class Node;
    class Expression
    {
        friend class Factory;
        public:
            Expression( Node* parent = NULL): parent_(parent){}
            virtual ~Expression();
            virtual int evaluate() const = 0; // permet de calculer
            virtual void print(std::ostream &) const = 0;  // permet d'afficher l'expression
            virtual const Expression * parent() const {return parent_;}
        private:
            Node * parent_;
    };
    Cette classe sera avantageusement dérivée en deux autres classes qui représenteront les feuilles de l'arbre pour l'une et les branche de l'arbre pour l'autre: les classes Leaf et Node, qui ressembleraient à quelque chose comme
    Code : 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
    class Leaf : public Expression
    {
        public:
            Leaf(Node * parent = ):Expression(parent){}
            virtual ~Leaf();
    };
    class Node : public Expression
    {
        public:
            Node(Node* parent = NULL):Expression(parent),
                                    left_(NULL), right_(NULL){}
           virtual ~Node()
           {
               delete left_;
               delete right_;
           }
           void setLeft(Expression * newLeft)
           {
                // on ne peut définir l'enfant que s'il n'y a pas encore été défini
                // et si la valeur qu'on veut lui donner est un enfant valide
               assert(! left_);
               assert( newLeft_);
               left_ = newLeft;
           }
     
           void setRight(Expression * newRight)
           {
                // on ne peut définir l'enfant que s'il n'y a pas encore été défini
                // et si la valeur qu'on veut lui donner est un enfant valide
               assert(! right_);
               assert( newRight);
               right_ = newRight;
           }
           /* on peut évaluer ce qui se trouve à gauche d'une part et ce qui
            * se trouve à droit d'autre part :D
            */
          int evaluateLeft() const{return left_->evaluate();}
          int evaluateRight() const{return right_->evaluate();}
        protected:
            Expression * left_;
            Expression * right_;
    };
    Les feuilles se subdivisent en deux grandes catégories :
    Les valeurs "fixes" (4, par exemple) d'un coté et les inconnues de l'autre("x", "y", "truc", "brol",...).

    L'évaluation d'une valeur fixe est relativement facile : il suffit de ... renvoyer la valeur en question (l'affichage est lui aussi assez facile, vu qu'il suffit d'afficher la dite valeur )

    Nous aurions donc une classe "Value" qui hérite de Leaf et qui ressemblerait à
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class Value : public Leaf
    {
        public:
            Value(int v, Expression * parent):Leaf(parent),value_(v){}
            virtual ~Value(){}
            virtual int evaluate() const{return value_;}
            virtual void print(std::ostream & oss) const
            { oss << value_ ;}
        private:
            int value_;
    };
    Pour ce qui est de l'inconnue, par contre, si l'affichage est tout simple (il suffit d'afficher le symbole), l'évaluation sera sans doute beaucoup plus compliquée car elle devra pour ainsi dire évaluer l'ensemble de l'expression complexe pour connaitre sa valeur (je te laisse le soin de réfléchir à l'algorithme ).

    Elle se présentera sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    class Unknown : public Leaf
    {
        public:
            Unknown(std::string const & symbol, Node * parent = NULL):Leaf(parent),symbol_(symbol){}
            virtual int evaluate() const
            {
                /* à toi de jouer :D */ 
            }
            virtual void print(std::ostream & oss)
            {
                oss << symbol_;
            }
        private:
            std::string symbol_;
    };
    Il nous reste les différents opérateurs à gérer, et, pour ce faire, nous allons les nommer de manière précise : Add (+), Substract (-), Divide (/), Multiply (*).

    En effet, chacun va évaluer différemment le résultat qu'il fournit, sous une forme proche de
    Code : 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
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    class Add : public Node
    {
        public:
            Add(Node * parent  = NULL):Node(parent){}
            virtual int evaluate() const
            {
                // on ne peut évaluer le noeud que si... les deux enfants existent ;)
                assert (left_);
                assert (right_);
                return left_->evaluate() + right_->evaluate();
            }
            virtual void print(std::ostream & oss) const
            {
                 // on ne peut afficher le noeud que si... les deux enfants existent ;)
                assert (left_);
                assert (right_);
                left_->print(oss);
                oss << " + ";
                right_->print(oss);
            }
    };
    class Substract : public Node
    {
        public:
            Substract(Node * parent  = NULL):Node(parent){}
            virtual int evaluate() const
            {
                // on ne peut évaluer le noeud que si... les deux enfants existent ;)
                assert (left_);
                assert (right_);
                return left_->evaluate() - right_->evaluate();
            }
            virtual void print(std::ostream & oss) const
            {
                 // on ne peut afficher le noeud que si... les deux enfants existent ;)
                assert (left_);
                assert (right_);
                left_->print(oss);
                oss << " - ";
                right_->print(oss);
            }
    };
    class Divide : public Node
    {
        public:
            Substract(Node * parent  = NULL):Node(parent){}
            virtual int evaluate() const
            {
                // on ne peut évaluer le noeud que si... les deux enfants existent ;)
                assert (left_);
                assert (right_);
                return left_->evaluate() / right_->evaluate();
            }
            virtual void print(std::ostream & oss) const
            {
                 // on ne peut afficher le noeud que si... les deux enfants existent ;)
                assert (left_);
                assert (right_);
                left_->print(oss);
                oss << " : ";
                right_->print(oss);
            }
    };
    class Multiply: public Node
    {
        public:
            Multiply(Node * parent  = NULL):Node(parent){}
            virtual int evaluate() const
            {
                // on ne peut évaluer le noeud que si... les deux enfants existent ;)
                assert (left_);
                assert (right_);
                return left_->evaluate() / right_->evaluate();
            }
            virtual void print(std::ostream & oss) const
            {
                 // on ne peut afficher le noeud que si... les deux enfants existent ;)
                assert (left_);
                assert (right_);
                left_->print(oss);
                oss << " * ";
                right_->print(oss);
            }
    };
    Hein? Quoi? Ah, j'ai oublié l'opérateur Equal (=)

    Mais non, je ne l'ai pas oublié... je l'ai simplement "laissé de coté" pour t'en parler à part

    Car l'affichage de cet opérateur devra sans doute afficher un "?" pour indiquer à l'élève qu'on attend la réponse (bon, ca, c'est pas bien compliqué ) mais, surtout, parce que la valeur renvoyée par Equal devrait, idéalement, correspondre à la valeur de l'inconnue.

    Et comme il n'y aurait aucun plaisir à tout te donner, je vais te laisser réfléchir à l'algorithme qui te permettra d'obtenir ce résultat

    Mais bon, Equal ressemblera quand même à quelque chose comme
    Code : 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
    class Equal: public Node
    {
        public:
            Multiply():Node(NULL){}
            virtual int evaluate() const
            { 
                // A toi de jouer
            }
            virtual void print(std::ostream & oss) const
            {
                 // on ne peut afficher le noeud que si... les deux enfants existent ;)
                assert (left_);
                assert (right_);
                left_->print(oss);
                oss << " = ";
                right_->print(oss);
                oss << " ? "<< std::endl;
            }
    };
    Avec une telle hiérarchie de classe, une expression est tout à fait capable de s'évaluer elle-même et donc... de fournir une valeur qui correspond à l'inconnue recherchée.

    Ta classe exercice pourrait donc se simplifier sous une forme proche de
    Code : 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
    class Exercice
    {
        public=
            template <typename iter>
            Exercice(std::string const & problem, iter begin, iter end )  :possibilities_(begin, end)
            {
                 problem_ = factory().parse(problem);
            }
            ~Excercice(){delete problem_;}
            void print(std::ostream & oss) const
            {
                problem_->print(oss);
                for(size_t i = ;i<possibilities_.size(); ++i)
                {
                   oss<< i+1<<" : " <<possibilities_[i]<<std::endl;
                }
            }
            bool correction(size_t choice) const
            {
                bool result = (possibilities_[choice-1] == problem_->evaluate();
                if(result)
                {
                    // que faire si c'est juste
                }
                else
                {
                    // que faire si c'est faux
                }
                return result;
            }
            private:
            Equal * problem_;
            std::vector<int> possibilities_;
    };
    Tu auras sans doute remarqué la présence dans le code de Factory().parse(problem), ainsi que la ligne friend class Factory; dans la classe Expression...

    Le role de la classe (et de sa fonction parse) sera de transformer une chaine de caractères du genre de "3 + 1 = Y" en un expression dont la racine est le symbole "=".

    Mais ca, je vais aussi te laisser y réfléchir (sinon, tu n'apprendra rien )

    L'idée est, en tout état de cause que, une fois que tu as créé une expression, si tu invoque la méthode print en lui passant une un flux de type stringstream, tu devrais récupérer une chaine de caractères correspondant (au ? près) à la chaine que tu aura parsée
    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

  3. #3
    Membre éclairé Avatar de Nono Sto
    Profil pro
    Inscrit en
    Mars 2009
    Messages
    350
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2009
    Messages : 350
    Par défaut
    Merci beaucoup, je vais lire cela très attentivement cette après midi

  4. #4
    Membre éclairé Avatar de Nono Sto
    Profil pro
    Inscrit en
    Mars 2009
    Messages
    350
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2009
    Messages : 350
    Par défaut
    Aie, aie, aie....

    tous cela m'est très compliqué, cependant je commence à dégagé la logique de ton code sauf un point qui me parait très obscur:

    dès le premiers bout de code:

    la classe node herite d'expression et demandais à la construction de celui ci.je comprend pas,

    La classe node qui herite d'expressions serait elle pas une classe branche ?

    Je suis perdu

  5. #5
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par Nono Sto Voir le message
    Aie, aie, aie....

    tous cela m'est très compliqué, cependant je commence à dégagé la logique de ton code sauf un point qui me parait très obscur:

    dès le premiers bout de code:

    la classe node herite d'expression et demandais à la construction de celui ci.je comprend pas,

    La classe node qui herite d'expressions serait elle pas une classe branche ?

    Je suis perdu
    oui, tout à fait...

    En fait, pour tout ce qui est du domaine des arbres et des graphes, on parle plus volontiers de noeud que de branches, mais ce qui importe surtout, c'est de faire la distinction entre un "noeud terminal" (une "feuille") et un noeud "non terminal" (une "branche", ou "un noeud")

    Ceci dit, je comprend parfatiement ton interrogation, car mes explications sont en francais et le code fourni est plutot anglophone
    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

  6. #6
    Membre éclairé Avatar de Nono Sto
    Profil pro
    Inscrit en
    Mars 2009
    Messages
    350
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2009
    Messages : 350
    Par défaut
    Donc il ya bien deux classe disctincte, une Node, et l'autre on vas dire "noeud non terminal"?

Discussions similaires

  1. Model POO pour un jeu de calcul mental
    Par Nono Sto dans le forum C++
    Réponses: 2
    Dernier message: 16/09/2011, 04h15
  2. Modele code pour importation requete ds Excel
    Par AMVBA dans le forum VBA Access
    Réponses: 7
    Dernier message: 15/06/2007, 09h29
  3. Réponses: 1
    Dernier message: 20/10/2006, 12h23
  4. POO : pour le meilleur et oublions le pire
    Par punkscum dans le forum Langages de programmation
    Réponses: 11
    Dernier message: 24/04/2006, 23h53
  5. [Choix de langage] POO pour de multiples applications
    Par Shadowritter dans le forum Langages de programmation
    Réponses: 15
    Dernier message: 17/01/2005, 15h42

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo