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

 C++ Discussion :

opération mathématique à partir d'une variable de type string


Sujet :

C++

  1. #1
    Membre habitué
    Homme Profil pro
    sans emploi
    Inscrit en
    Février 2014
    Messages
    365
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Maine et Loire (Pays de la Loire)

    Informations professionnelles :
    Activité : sans emploi
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2014
    Messages : 365
    Points : 131
    Points
    131
    Par défaut opération mathématique à partir d'une variable de type string
    Bonjour

    J'ai une variable de type "string" composée de chiffres et d'opérateurs
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::string cadre_input_calcul = "1+2-3/4*5";
    Comment faire pour calculer l'opération?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    float operation= ??cadre_input_calcul??
    Cordialement

  2. #2
    Expert éminent
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 562
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Décembre 2015
    Messages : 1 562
    Points : 7 628
    Points
    7 628
    Par défaut
    Bonjour,

    Il n'existe aucune fonction qui évaluerait le texte dans une chaine de caractère. Il te faut un code qui "lise" la chaine, y détecte les nombres et symboles, et effectue alors les actions nécessaires.

  3. #3
    Membre habitué
    Homme Profil pro
    sans emploi
    Inscrit en
    Février 2014
    Messages
    365
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Maine et Loire (Pays de la Loire)

    Informations professionnelles :
    Activité : sans emploi
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2014
    Messages : 365
    Points : 131
    Points
    131
    Par défaut
    merci

    pour détecter les nombres
    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
     
    #include <stdio.h>
    #include <string>
    #include <sstream>
    #include <typeinfo>		// pour typeid
    #include <ctype.h>      // pour isdigit()
    #include <iostream>		// pour cout
    #include <regex>
     
     
    int main( int argc, char* args[] )
    {
        const std::string string3 = "9,42+308,7-41+36*234,56/546+7.56*769,3";
        std::regex  regex3("([0-9]+[.|,]{0,1}[0-9]*)");  
        std::smatch match3;
        if (regex_search(string3, match3, regex3))
        {
            for (int i=1; i<match3.size(); i++) 
            {
                std::cout << match3[i] << std::endl;
            }
        }
    return 0;
    }
    problème: il ne fait apparaitre que le premier nombre: 9,42

  4. #4
    Expert éminent sénior
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 627
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 627
    Points : 10 551
    Points
    10 551
    Par défaut
    Sur la documentation cplusplus.com std::regex_search, il faut relancer la recherche.
    Et toutes les entêtes C comme stdio.h ont 1 version C++ cstdio (on retire l'extension .h et on préfixe 1 c)

    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
    #include <iostream>
    #include <regex>
     
    #include <cstdlib>
     
     
    int main(int argc, char* args[])
    {
        std::string string3 = "9,42+308,7-41+36*234,56/546+7.56*769,3";
        std::regex  regex3("([0-9]+[.|,]{0,1}[0-9]*)");  
        std::smatch match3;
     
        while( regex_search(string3, match3, regex3) ) {
            for (auto x:match3) { std::cout << x << " "; }
            std::cout << std::endl;
     
            string3 = match3.suffix().str();
        }
     
     
        return EXIT_SUCCESS;
    }

  5. #5
    Membre habitué
    Homme Profil pro
    sans emploi
    Inscrit en
    Février 2014
    Messages
    365
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Maine et Loire (Pays de la Loire)

    Informations professionnelles :
    Activité : sans emploi
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2014
    Messages : 365
    Points : 131
    Points
    131
    Par défaut
    merci

    je ne vois pas comment récupérer les valeurs sous forme terme_facteur[i]=x[i]

  6. #6
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 611
    Points
    30 611
    Par défaut
    Salut,

    A vrai dire, les expressions régulières ne te seront pas d'un très grand secours pour résoudre ton problème. Je m'explique:

    Une expression régulière va parcourir une "chaine de caractères d'origine" à la recherche d'un patron, représenté sous la forme d'une chaine de caractères, qui permettra à l'expression régulière d'extraire une "sous chaine de caractères" de la "chaine d'origne" si le patron recherché est trouvé.

    C'est certes très pratique, mais... cela ne remplit certainement pas l'ensemble de nos besoins, car ceux-ci vont "un cran plus loin" en nécessitant de convertir les chaines de caractères qui représentent des nombres en valeurs numériques.

    Attention, je ne dis absolument pas que l'on ne peut en aucun cas avoir recours aux expressions régulières. Je dis juste que c'est peut être se faire "beaucoup de mal pour rien", car les moyens dont on dispose pour convertir une chaine de caractères en valeur numérique ne nécessitent absolument pas que la chaine convertie ne soit composée que du nombre à convertir.

    De manière générale, pour extraire les valeurs numériques d'une chaine de caractères en C++, tu dois utiliser les fonctions std::sto* (std::stoi et autres pour les entiers, std::stof, std::stod, et std::stold pour les réels).

    Le deuxième paramètre (le premier qui soit optionnel) nous permet de fournir un pointeur sur un entier qui correspondra à la position du premier caractère de la chaine de caractères qui "ne fait clairement pas partie du nombre".


    Par contre, il faudra jouer avec les "sous-chaines de caractères" afin de pouvoir récupérer les différents opérateurs et, au delà, l'expression complète que l'on souhaite évaluer.

    Par contre, il va aussi falloir prendre en compte le fait que la partie décimale d'un nombre en informatique est représentée par le point "." et non par la virgule ","

    Ainsi, si on veut traiter une chaine de caractères (que je vais utiliser tout au long de cette explication) qui prend la forme de " std::string str = "9,42+308,7-41+36*234,56/546+7.56*769,3";, les deux premières choses que nous voudrons faire sont:
    1. effectuer une "copie de travail" de la chaine, afin de ne pas aller modifier la chaine "d'origine"
    2. remplacer toutes les virgules qui apparaissent par des points, histoire que nos fonctions de conversion y "retrouvent leur jeunes".

    La première étape est toute simple, car il nous suffira d'une ligne proche de
    pour avoir une variable nommée copy qui est la copie conforme de la chaine de caractères str.

    La deuxième étape n'est a priori pas beaucoup plus compliquée, car nous disposons d'une fonction d'algorithme nommé std::replace_if capable de le faire pour nous (il faut juste penser à inclure le fichier d'en-tête <algorithm>). Une ligne proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::replace_if(copy.begin(), copy.end(), [](char c){return c==',';},'.');
    devrait faire l'affaire
    Je sais, elle a l'air compliquée comme cela, mais elle en fait, elle est toute simple et parfaitement logique... si tu ne la comprends pas, n'hésite pas à me le dire, je t'expliquerai le tout au besoin

    Bien, maintenant que nous avons la certitude que la chaine de caractères (copy) pourra être traitée correctement avec la fonction std::stof, il est peut être utile de prendre trente secondes pour réfléchir à notre situation actuelle et au résultat que l'on souhaite obtenir...

    Notre situation actuelle est que nous sommes désormais face à une chaine de caractères dont on peut clairement dire qu'il s'agit "d'une expression mathématique" (complexe).

    Notre objectif de départ étant de récupérer les différentes valeurs numériques que cette expression contient, ainsi que ... les opérateurs mathématiques qui séparent ces différentes valeurs (ce sera plus facile, semblet-t-il, pour atteindre l'objectif final ).

    L'objectif final étant de pouvoir évaluer le résultat de cette expression une fois que toutes les opérations auront été effectuées. Accessoirement, s'il y avait moyen de respecter les règles de priorités pour les différents opérateurs, ce serait franchement pas mal

    Tiens, au fait... Je viens d'introduire quelques notions sympa ici, dont la notion d'"opérateurs mathématiques". Comme ces opérateurs seront essentiels au calcul du résultat final, nous serions peut être bien inspirés d'introduire cette notion dans notre "DSL" (acronyme de Domain Specific Language ou, si tu préfères, notre langage spécifique au domaine de notre projet).
    Le plus simple serait sans doute de créer une énumération reprenant les quatre opérations de base et qui serait proche
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    enum MathOperator{
        plus,
        minus,
        multiply,
        divide,
        max
    };
    La valeur "max" n'est présente ici que pour une question de facilité, car elle nous permettra par la suite des choses sympa ... ou non

    Et, bien sur, on pourrait fournir une fonction qui fournisse la valeur énumérée correspondant au symbole représenté par le caractère en question:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    constexpr MathOperator mathFromChar(char c){
        switch c{
            case '+' : return plus;
            case '-' : return minus;
            case '*' : return multiply;
            case '/' : return divide;
            default : assert(false && You should never come here");
        }
        return max;
    }
    Cette fonction est déclarée comme étant constexpr uniquement parce que cela permettra au compilateur de faire quelques optimisations supplémentaires

    Maintenant que nous avons une fonction pour extraire un float (je parle bien sur de std::stof) et une autre fonction pour récupérer le type d'opérateur mathématique utilisé dans le cadre de "l'expression mathématique dont on veut calculer le résultat, ben ... YAPUKA ... mettre tout cela en musique

    Pour se faire, nous aurons encore besoin de deux variables particulières:
    • un float, d'abord, pour pouvoir extraire les réels représentés par la chaine de caractères et
    • un size_t ensuite, pour savoir la position du "premier caractère n'ayant pas été utilsé par la fonction.

    le float peut être déclaré au moment où l'on récupère le résultat de std::stof, par contre le size_t devra être déclaré avant (car on doit en prendre l'adresse pour la transmettre à stof...).

    Cela pourrait prendre une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    size_t position{0};
    float extracted = std::stof(copy, &position);
    Nous pouvons "assez facilement" utiliser la nouvelle valeur de position pour récupérer l'opérateur mathémathique sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    MathOperator oper = mathFromChar(copy[position]);
    Bien sur, nous ne devons donc pas oublier que le premier caractère qui nous intéresse se trouve désormais à position +1, mais il est désormais temps de supprimer "tout ce qui a déjà été traité" de la chaine:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    copy = copy.substr(position + 1);
    Nous sommes désormais rendus au point où
    1. nous avons extrait la première valeur numérique de la chaine de caractères
    2. nous avons extrait le premier opérateur mathématique de la chaine de caractères
    3. nous avons supprimé de la chaine de caractères tous les caractères qui ont déjà été utilisés lors des deux extractions sus-citées.

    Ce qu'il faut désormais, c'est "continuer sur cette lancée" afin de parcourir toute la chaine de caractères... Et ca, je vais te laisser faire, autrement, cela n'aurait absolument aucun intérêt

    Dans l'idéal, il faudrait trouver le moyen de stocker toutes les informations obtenues, histoire que l'on puisse les manipuler "à notre aise" ... après avoir parcouru toute la chaine de caractères

    Fais attention au fait qu'il y a peut être une petite astuce à la fin de la chaine de caractères, étant donné qu'elle est censée se cloturer par ... une valeur numérique

    Une fois que nous aurons parcouru l'ensemble de la chaine de caractères, il faudra trouver le moyen de "recréer" l'expression mathématique qu'elle représentait de manière à pouvoir en calculer le résultat final.

    Soit toujours bien attentif au fait que la multiplication et la division ont priorité sur l'addition lorsque tu évalue le résultat final.

    Une fois que tu en sera rendu là, nous pourrons éventuellement améliorer le tout en ajoutant le support d'espaces "surnuméraires" (par exemple avant et après un opérateur mathématique), mais ... à chaque jour suffit sa peine, n'est ce pas?
    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

  7. #7
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 113
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 113
    Points : 32 958
    Points
    32 958
    Billets dans le blog
    4
    Par défaut
    Ça ressemble furieusement à un exercice qu'on faisait déjà il y a 20 ans en école.
    Il n'y a bien sûr pas de solution miracle parce que le but est justement de programmer une solution.
    L'objectif est de parcourir la chaîne de caractère et extraire les informations pour recréer les nombres et opérations.
    Je me souviens avoir fait ça avec des opérations en notation polonaise inversée.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  8. #8
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 518
    Points
    41 518
    Par défaut
    La partie problématique, c'est généralement la prise en compte des priorités opératoires. Sans ça, c'est presque trivial.
    Citation Envoyé par Bousk
    Je me souviens avoir fait ça avec des opérations en notation polonaise inversée.
    Une opération saisie en NPI facilite grandement les choses, vu que le programme n'a plus à se préoccuper des priorités vu que l'utilisateur l'a fait pour lui: Il itère son algorithme contre sa pile et c'est tout. Par contre, si on demandait de convertir une opération depuis "9,42+308,7-41+36*234,56/546+7.56*769,3" vers de la NPI...

    PS: la même opération en NPI devrait être:

    9,42 308,7 + 41 - 36 234.45 * 546 / + 7,56 769,3 * +
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  9. #9
    Expert éminent sénior
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 627
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 627
    Points : 10 551
    Points
    10 551
    Par défaut
    C'est 1 exercice classique parce qu'il faut programmer 1 machine à états/ automate.
    En plus le résultat intermédiaire est 1 arbre [peut-être binaire] les nœuds sont les opérateurs, les feuilles sont les valeurs, et 1 parcours postfixe pour lire le résultat

    Avec par exemple [en gros mais sûrement faux]
    pour les mots :
    • mul_div = * ou /
    • add_sub = + ou -
    • valeur = [0..9]+[.[0..9]*]*


    pour la grammaire :
    • expression = term ou term add_sub expression
    • term = valeur ou valeur mul_div term


    Et ensuite, il y a de grandes chances qu'on va te demander de gérer certains "nombres magiques" (comme pi), les signes, les parenthèses, les puissances, les fonctions, ...

  10. #10
    Membre habitué
    Homme Profil pro
    sans emploi
    Inscrit en
    Février 2014
    Messages
    365
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Maine et Loire (Pays de la Loire)

    Informations professionnelles :
    Activité : sans emploi
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2014
    Messages : 365
    Points : 131
    Points
    131
    Par défaut
    J'ai essayé avec vector<int>. Mon code fonctionne avec le std::string str = "9,42+308,7-41+36*234,56/546+7.56*769,3+3" de l'exemple mais n'est pas générique. Pouvez vous m'aidez?


    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
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
     
    #include <string>
    #include <iostream>
    #include <regex>
    #include <typeinfo>
    #include <cassert>
    #include <list>
    #include <numeric>  // accumulate
     
    using namespace std;
     
    // énumération reprenant les quatre opérations de base
    enum MathOperator{
        addition,
        soustraction,
        multication,
        division,
        vide
    };
     
    constexpr MathOperator mathFromChar(char c){
        switch (c)
        {
            case '+' : return addition;
            case '-' : return soustraction;
            case '*' : return multication;
            case '/' : return division;
        }
        return vide;
    }
     
    int main()
    {
        std::string str = "9,42+308,7-41+36*234,56/546+7.56*769,3+3";
        // copie du string
        std::string copie_de_str {str};  // <=> std::string copie_de_str = str;
        // remplacer les , par des .
        std::replace_if(copie_de_str.begin(), copie_de_str.end(), [](char c){return c==',';},'.');
        cout << "std::replace_if: " << copie_de_str << endl;
        int longueur_initiale = copie_de_str.length();
        cout << "longueur_initiale: " << longueur_initiale << endl;
        // savoir la position du "premier caractère n'ayant pas été utilsé par la fonction
        size_t position{0};
        cout << "size_t position{0}: " << position << endl;    
     
        // longueur de la chaine restante
        int difference = longueur_initiale-position;
        int difference_bis = 0;    
     
        // tableau de termes ou facteurs
        float * tableau_terme_facteur = new float;    // allouer de la mémoire
        int i = 0;
     
        // tableau d operateurs
        int * tableau_operateur_calcul = new int;
        int j = 0;
     
        cout << endl;
     
        // calcul de l operation
        float resultat_final = 0;
     
        // conteneur des termes ou facteurs
        vector<float> vector_terme_facteur;
        // conteneurs des operateurs
        vector<int> vector_operateur;
     
        while (difference)
        {
            // affichage index
            cout << "i: " << i << endl;
     
            //  extraire les réels représentés par la chaine de caractères
            float extracted_terme_facteur = std::stof(copie_de_str, &position);
            cout << "extracted_terme_facteur: " << extracted_terme_facteur << endl;
            cout << "size_t position: " << position << endl; 
            // tableau des termes ou facteurs
            tableau_terme_facteur[i] = extracted_terme_facteur;
     
            int difference_bis = longueur_initiale - (position+1);
            cout << "difference_bis: " << difference_bis << endl;  
     
            // utiliser la nouvelle valeur de position pour récupérer l'opérateur mathémathique
            cout << "copie_de_str[position]: " << copie_de_str[position] << endl;
            MathOperator extracted_operateur_calcul = mathFromChar(copie_de_str[position]);
            // tableau des operateurs
            tableau_operateur_calcul[j] = extracted_operateur_calcul;
     
            cout << "MathOperator oper: " << extracted_operateur_calcul << endl;
            cout << "position+1: " << position + 1 << endl;
     
            // caracteres restants apres extraction termes/facteurs et opérateur
            if(difference_bis>=0)
            {
                copie_de_str = copie_de_str.substr(position + 1); // +1 <=> 1 caractere pour l operateur
                // substr(): Copie une sous-chaîne d'un certain nombre de caractères dans une chaîne qui commence à la position spécifiée
                cout << "copie_de_str: " << copie_de_str << endl;
            }
            // nombre total de caracteres restants dans la chaine
            int longueur_bis = copie_de_str.length();
            cout << "longueur_bis: " << longueur_bis << endl;
            difference = longueur_bis-position;
            cout << "difference: " << difference << endl << endl;
     
            longueur_initiale = difference_bis;
     
            // incrementation index
            i++;
            j++;  
        }
     
        // affichage tableau_terme_facteur
        for(int j=0; j<sizeof(tableau_terme_facteur)+1; j++)
        {
            cout << "tableau_terme_facteur["<<j<<"]: " << tableau_terme_facteur[j] << endl;
        }
        cout << endl;
        cout << "sizeof(tableau_terme_facteur): " << sizeof(tableau_terme_facteur) << endl;
     
        // affichage tableau_operateur_calcul
        for(int j=0; j<sizeof(tableau_operateur_calcul)+1; j++)
        {
            cout << "tableau_operateur_calcul["<<j<<"]: " << tableau_operateur_calcul[j] << endl;
        }
        cout << endl;
        cout << "sizeof(tableau_operateur_calcul): " << sizeof(tableau_operateur_calcul) << endl;
     
        // vector_terme_facteur 
        for(i=0; i < sizeof(tableau_terme_facteur)+1; i++)
        {
            vector_terme_facteur.push_back(tableau_terme_facteur[i]);
        }
        for(i=0; i < vector_terme_facteur.size(); i++)
        {
            cout << "vector_terme_facteur["<<i<<"]: " << vector_terme_facteur[i] << endl;
        }
     
        // vector_operateur 
        for(j=0; j < sizeof(tableau_operateur_calcul); j++)
        {
            vector_operateur.push_back(tableau_operateur_calcul[j]);
        }
        for(j=0; j < vector_operateur.size(); j++)
        {
            cout << "vector_operateur["<<j<<"]: " << vector_operateur[j] << endl;
        }
     
        // priorité a la multiplication
        for(j=0; j < vector_operateur.size(); j++)
        {
            if(vector_operateur[j]==2)
            {
                vector_terme_facteur[j] = vector_terme_facteur[j] * vector_terme_facteur[j+1];
                // supression de l element j+1 du vector_terme_facteur
     
                vector_terme_facteur.erase( vector_terme_facteur.begin()+(j+1));
                // supression de l element j+1 du vector_operateur
                vector_operateur.erase( vector_operateur.begin()+j);
            }
        }
     
        // verification suite multiplication
        cout << "verification de la multiplication" << endl;
        for(i=0; i < vector_terme_facteur.size(); i++)
        {
            cout << "vector_terme_facteur["<<i<<"]: " << vector_terme_facteur[i] << endl;
        }
        for(j=0; j < vector_operateur.size(); j++)
        {
            cout << "vector_operateur["<<j<<"]: " << vector_operateur[j] << endl;
        }
     
        // priorité a la division
        for(j=0; j < vector_operateur.size(); j++)
        {
            if(vector_operateur[j]==3)
            {
                vector_terme_facteur[j] = vector_terme_facteur[j] / vector_terme_facteur[j+1];
                // supression de l element j+1 du vector_terme_facteur
     
                vector_terme_facteur.erase( vector_terme_facteur.begin()+(j+1));
                // supression de l element j+1 du vector_operateur
                vector_operateur.erase( vector_operateur.begin()+j);
            }
        }
        // verification suite division
        cout << "verification de la division" << endl;
        for(i=0; i < vector_terme_facteur.size(); i++)
        {
            cout << "vector_terme_facteur["<<i<<"]: " << vector_terme_facteur[i] << endl;
        }
        for(j=0; j < vector_operateur.size(); j++)
        {
            cout << "vector_operateur["<<j<<"]: " << vector_operateur[j] << endl;
        }
     
        // priorité a la soustraction
        for(j=0; j < vector_operateur.size(); j++)
        {
            if(vector_operateur[j]==1)
            {
                vector_terme_facteur[j] = vector_terme_facteur[j] - vector_terme_facteur[j+1];
                // supression de l element j+1 du vector_terme_facteur
     
                vector_terme_facteur.erase( vector_terme_facteur.begin()+(j+1));
                // supression de l element j+1 du vector_operateur
                vector_operateur.erase( vector_operateur.begin()+j);
            }
        }
        // verification suite soustraction
        cout << "verification de la soustraction" << endl;    
        for(i=0; i < vector_terme_facteur.size(); i++)
        {
            cout << "vector_terme_facteur["<<i<<"]: " << vector_terme_facteur[i] << endl;
        }
        for(j=0; j < vector_operateur.size(); j++)
        {
            cout << "vector_operateur["<<j<<"]: " << vector_operateur[j] << endl;
        }    
     
        // priorité a l addition
        float sum = 0;
        for (i=0; i<vector_terme_facteur.size(); i++)
        {
            sum += vector_terme_facteur[i];
        }
        cout << "resultat final: " << sum;
     
    	return 0;
     
    }

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 611
    Points
    30 611
    Par défaut
    Pas mal!!!

    Cependant, il y a, en effet place pour du progrès . Car, pour être honnête, tu as énormément de chance que ton application ne plante pas lamentablement...

    Allez, quelques conseils parmi d'autres:
    1- (niveau : détail ) on n'inclut que les fichiers d'en-tête dont on a vraiment besoin
    L'instruction préprocesseur #include <nom_de_fichier> ne fait, en définitive, que recopier (de manière récursive) le code qui se trouve dans le ficher indiqué là où l'instruction se trouve dans ton code à toi.

    Or, tout ce code "rajouté" par le préprocesseur devra ... être analysé par le compilateur lorsqu'il prendra le relais. Et, bien sur, si tu n'utilises pas les fonctionnalités déclarées par le fichier inclus, tu feras tout simplement travailler le compilateur ... pour rien.

    Cette analyse va prendre du temps. Beaucoup de temps. Tu vas donc demander au compilateur de prendre "beaucoup de temps" à faire un travail qui ne servira strictement à rien.

    Ce n'est pas catastrophique ici, car le code reste malgré tout "très simple". Mais, dans les projets "très importants" (composés de plusieurs centaines de milliers de fichiers), cela peut représenter un temps supplémentaire pour la compilation absolument considérable. Il vaut donc mieux prendre directement l'habitude de n'inclure que "le stricte nécessaire", mais guère plus


    Tu n'utilises aucune des fonctionnalités fournies par les fichiers d'en-tête <regex>, <typeinfo>, <cassert> ou <list>. Tu n'as donc pas besoin de les inclure ici.

    Par contre, tu n'as pas inclut le fichier d'en-tête <vector>, qui apporte la classe std::vector que tu utilises. Par chance, il semblerait que ce fichier soit inclut de manière récursive par l'un des fichiers "inutiles" que tu as inclut, tout en rajoutant une quantité considérable de code "en trop"

    Les seuls fichiers dont tu aurais besoin ici sont, en définitive:
    • <iostream> pour std::cout
    • <string> pour la classe std::string (et la fonction std::stof)
    • <vector> pour la classe std::vector
    • et <numeric> pour la fonction std::accumulate

    Eventuellement, si tu décidais d'utiliser les assertions (assert(condition_a_vérifier)) tu pourrais envisager d'ajouter le fichier <cassert à cette liste
    2- (niveau : détail ) Les commentaires qui ne font que paraphraser ce que le code indique déjà sont totalement inutiles:

    Ainsi, tes commentaire // copie du string et // <=> std::string copie_de_str = str; sont totalement inutile, car ils ne font que répéter (deux fois, qui plus est !!! ) ce que le code std::string copie_de_str {str}; dit déjà de manière très claire, d'autant plus que tu as eu le bon ton de choisir un nom pour la variable qui soit particulièrement explicite

    Par contre, le commentaire // remplacer les , par des . est d'une certaine manière "plus intéressant", car il décrit non pas ce que ton code fait,mais ce que tu cherches à faire avec le code qui suit.

    Si donc, le troisième commentaire semble (relativement justifié), les deux premiers auraient parfaitement pu ne pas avoir été écrits car ils n'apportent rien de "particulièrement intéressant"

    Le commentaire suivant (// savoir la position du "premier caractère n'ayant pas été utilisé par la fonction) est encore plus intéressant pour mon explication car il se trouve "entre les deux" situations que nous venons de croiser:

    D'une part, il indique clairement l'usage que tu veux faire de la variable position, tout en ne paraphrasant pas "tout à fait" le code qui le suit.

    Peut-être aurais tu pu éviter ce commentaire en choisissant "un autre nom" pour la variable Peut être aurait il "simplement" suffit de rajouter "quelque chose" au nom "position" pour rendre ce commentaire tout à fait inutile

    La même remarque pourrait d'ailleurs s'appliquer au commentaire // longueur de la chaine restante.

    3- (niveau : détail) la directive using namespace std; n'a rien à faire ici
    n'ayant pas trop envie de répéter "une fois encore" la raison de cette remarque, je te proposerais simplement d'aller lire ==>cette intervention<== de ma part qui explique clairement pourquoi il ne faut pas l'utiliser.

    Dans le cas présent, c'est d'autant plus vrai que tu ne profites même pas de la "facilité" apportée par cette directive, vu que tu utilises déjà correctement le nom pleinement qualifié de la plupart les fonctionnalités offertes par la bibliothèque standard, à savoir:
    • std::string
    • std::stof
    • std::vector

    Au final, il n'y a guère que pour cout (qui devrait être std::cout) et pour endl (qui devrait être std::endl) que tu utilise cette possibilité.

    L'un dans l'autre, il faudrait de toutes manières essayer "d'homogénéiser" les pratiques: ou bien, tu utilises le nom pleinement qualifié pour tout, ou bien tu profite de la facilité offerte par la directive using namespace std;. Mais il faut être "un tout petit peu cohérent" dans ce que l'on fait, et ne pas commencer à utiliser une possibilité pour "certaines choses" et l'autre "pour le reste"

    (Tu auras compris à quelle solution va ma préférence personnelle )

    4- (niveau : critique) Pas d'allocation dynamique de la mémoire manuelle s'il n'y a pas d'héritage public

    Et encore: même lorsqu'il y a un héritage public, il y a moyen de se passer de l'allocation dynamique de la mémoire manuelle dans certaines circonstances.

    Et si les circonstances font qu'il est vraiment impossible de se passer de l'allocation dynamique de la mémoire, il est impératif de savoir ce que l'on fait et de s'entourer d'énormément de précautions pour éviter que cela ne tourne à la catastrophe.

    C++ fournit amplement assez de fonctionnalités que pour pouvoir élever cette simple phrase en règle générale qui devrait être respectée de tous temps, sauf situation vraiment particulière.

    La meilleure preuve en est que tu as d'ailleurs malheureusement "tout faux" dans la manière dont tu l'utilises:
    a- La chaîne que tu essaye d'analyser est composée de neuf facteurs et de huit opérateurs, mais tu essayes de faire rentrer "toutes ces données" dans l'espace demandé au système d'exploitation (au travers de new) suffisant pour représenter ... une seule (et unique !!!) donnée. Comment comptes tu t'y prendre pour faire rentrer toutes ces données dans un si petit espace au chausse-pied

    Quand on demande au système d'exploitation de nous allouer de la mémoire (au travers de new), on prend la responsabilité de rendre cette mémoire au système d'exploitation quand on n'en a plus besoin. Si on perd la variable (de type pointeur) représentant l'adresse de la mémoire en question avant de l'avoir rendue a système d'exploitation, on observe une "fuite mémoire": cette mémoire ne pourra plus être utilisée par l'application en cours (vu qu'on ne sait plus y accéder) alors que le système d'exploitation continue à considérer cette mémoire comme "inutilisable" de son point de vue (vu qu'elle est toujours considérée comme "utilisée par l'application").

    Si le cas se reproduit "suffisamment souvent", on peut carrément en arriver à un point où le système d'exploitation ne dispose plus d'assez de mémoire que pour assurer son propre travail (car lui aussi, il a besoin de mémoire pour travailler), et c'est donc "tout le système" qui risque de s'effondrer à un moment ou à un autre.

    Dans le cas présent, tu as de la chance: ton application ne va pas tourner "pendant des heures", et le système d'exploitation récupérera "par la porte ou par la fenêtre" toute la mémoire qu'il a allouée à l'application lorsque celle-ci s'arrêtera

    Le "peut de mémoire" que l'on perd ne sera donc pas perdu pendant très longtemps. Tu auras cependant compris que c'est le genre d'erreur que l'on ne peut pas se permettre de laisser passer, car, si tu en viens à la reproduire "à peine plus souvent", dans un code "à peine plus complexe", pour une application qui devra travailler pendant "à peine plus longtemps" (mettons: quelques heures ou quelques jours), ce sera une véritable catastrophe.

    C'est d'autant plus vrai que tes variables tableau_terme_facteur et tableau_operateur_calcul ne servent en réalité absolument à rien, vu y introduit "au chausse-pied" des données qui correspondent à celles que tu récupère au travers des variables extracted_terme_facteur et extracted_operateur_calcu dans le seul but ... d'attendre que tu te décides "enfin (!!) " à les ajouter à tes tableaux de type std::vectorMais, alors, pourquoi pas directement ajouter les valeurs de extracted_terme_facteur et de extracted_operateur_calcu respectivement dans vector_terme_facteur et vector_operateur

    5- (niveau: danger) sizeof ne fait pas ce que tu crois

    Au vu de ton utilisation que tu en fais, je suis quasiment persuadé (même si je peux me tromper) que tu penses te renvoie "comme par magie" le nombre d'éléments auxquel un pointeur ( comme extracted_terme_facteur ou extracted_operateur_calcul) donne accès.

    Ce n'est absolument pas le cas!!!

    l'opérateur sizeof nous renvoie le nombre de bytes (d'octets) qui est nécessaire pour permettre de représenter l'intégralité d'une donnée quelconque.

    Dans le cas présent, si sizeof(extracted_terme_facteur[) te renvoie 8, c'est uniquement parce que extracted_terme_facteur est un pointeur (sur une donnée de type float, en l'occurrence) et qu'un pointeur est en réalité une valeur numérique entière, généralement non signée, dont la taille (le nombre de bytes / d'octets) qui la compose permet permet de représenter l'ensemble des adresses mémoire (potentiellement) accessibles sur le système sur lequel l'application s'exécute.

    Tu utilises, de toute évidence, une architecture 64 bits. Cela implique que le type "pointeur" est composé de ... 8 bytes (ou octets).

    6- (niveau: détail) Evites les instructions inutiles

    On a tout à perdre à ajouter des instructions inutiles:
    • cela nous prend du temps pour les écrire
    • cela prend du temps au compilateur pour les analyser et en fournir le code binaire exécutable correspondant
    • cela prend du temps au processeur pour exécuter ces instructions lorsque le programme fonctionne
    • cela augment (grandement) le risque d'erreurs et de bugs
    • cela rend le code plus complexe, qui demande de ce fait plus de temps afin de l'analyser
    • cela rend le code plus difficile à comprendre, et, le cas échéant, à corriger / faire évoluer

    Car, comme l'avait si bien décrit le créateur de la 2CV: ce qui est absent ne peut pas casser

    Toute la partie dans laquelle tu stocke provisoirement les valeurs de extracted_terme_facteur et extracted_operateur_calcu dans (respectivement) tableau_terme_facteur et tableau_operateur_calcul avant de les stocker dans vector_terme_facteur et vector_operateur est inutile, et, en plus, à l'origine d'un bug majeur (à cause de la mauvaise utilisation que tu fait de new)

    Retiens qu'un code sera plus souvent lu / analysé / corrigé qu'il ne sera compilé et / ou exécuté. Cela signifie que le premier interlocuteur auquel ton code est destiné n'est pas le compilateur, mais bien ... l'humain qui doit le lire.

    Le compilateur sera suffisamment buté et manquera suffisamment d'imagination que pour t'indiquer clairement ce qu'il ne comprend pas, alors que l'humain qui doit lire ton code pourrait arriver à en tirer des conclusions qui ne sont pas celles auxquelles tu pensais toi-même.

    Plus tu peux garder ton code simple, plus tu limite le risque d'erreur et de mauvaise compréhension.

    En un mot, une certaine forme de développement utilise l'acronyme KISS : Keep It Simple, Stupid (garde cela simple, idiot). Voilà bien une "règle générale" supplémentaire à respecter en tous temps (et sans aucune situation particulière )

    7- (niveau: important) Limite la taille des fonctions

    Il fut un temps où les règles en entreprise ne se gênaient pas pour imposer une limite de cinquante ligne grand maximum par fonction. La raison "officielle" -- et la plus facilement défendable -- pour cette limite était liée aux limites des possibilités d'affichage de l'époque, car on disposait royalement de 25 lignes par 80 colonnes, et que cette limite autorisait déjà le fait d'avoir besoin de ... deux fois la surface d'affichage disponible pour (avoir une chance de) lire le contenu de l'ensemble de la fonction. Ce qui compliquait d'ailleurs déjà fortement la relecture du code

    Bien sur, les évolutions successives ont rendu cette contrainte matérielle obsolète. Et pourtant le problème persiste: lorsqu'une fonction est "trop grande", il devient très rapidement difficile de l'analyser "correctement".

    Je ne vais pas donner de limite "gravée dans le marbre" quant à la taille maximale des fonctions, car je suis persuadé qu'elle risquerait de t'inciter à avoir recours à de nombreuses techniques que je qualifierais de discutables pour arriver à respecter cette limite.

    Cependant, je considère généralement qu'une fonction qui ferait plus de 25 à 30 lignes (à peu près) est une fonction qui est déjà "trop longue".

    Alors, évidemment, quand je vois ta fonction main qui fait 200 lignes (commentaires compris, je te l'accorde), j'ai peur

    Les prochaines remarques devraient te permettre de résoudre "assez facilement" le problème

    6- (niveau: important) SRP : Single Responsability Principle (ou "Principe de la Responsabilité Unique)

    L'un des principes les plus importants et les plus immuable de la programmation, quel que soit le paradigme ou le langage utilisé, est très certainement le principe connu sous l'acronyme SRP, qui est l'abréviation anglaise de Single Responsability Principle (ou principe de la responsabilité unique).

    Ce principe nous dit -- pour faire simple -- que chaque "chose", chaque type de donnée, chaque donnée et chaque fonction ne devrait jamais avoir qu'une seule et unique responsabilité. Si "quelque chose" (une fonction, par exemple) se retrouve à avoir plus d'une seule (et unique) responsabilité, c'est que cette chose "en fait trop".

    A ce titre, ta fonction main en fait, décidément, beaucoup trop, car, elle s'occupe:
    1. (plusieurs) fois de l'affichage d'informations relatives à son "état d'avancement courant"
    2. de l'extraction (et du stockage temporaire) des termes et des opérateurs qui composent la chaîne de caractères
    3. de la copie des termes et opérateurs "temporaires" dans les tableaux "finaux"
    4. du calcul des produits des multiplications
    5. du calcul des quotients des divisions
    6. du calcul des sommes des additions
    7. du calcul des différence des soustractions.
    8. de l'affichage final

    cela nous fait donc huit responsabilités. Si même on considère que les responsabilités (2) et (3) peuvent aisément être réunies en une seule, ainsi que les responsabilité (5),(6) et (7), et en considérant comme "logique" que la responsabilité (8) est "inhérente" à la fonction principale, il reste malgré tout encore quatre responsabilités, ce qui en ferait au moins deux de trop...

    8- (niveau: conseil) L'affichage ne sert pas au débuggage

    Une grande partie de ton code ne sert qu'à afficher l'état actuel de ton programme. Ce n'est la la "bonne manière" de débugger. Tu serais bien inspiré de t'intéresser à un outil génial qui t'a été fourni avec ton compilateur: le débuggeur, ainsi qu'à son utilisation.

    Il permet souvent de se faire une idée bien plus correcte de ce qui se passe en mémoire (sans risque de se méprendre sur la signification de la valeur renvoyée par sizeof(), par exemple )

    Cela réduirait en outre considérablement la taille de ton code

    9- (niveau : détail) Les valeurs énumérées, c'est pas fait pour les chiens!

    Si je passe mon temps à définir une énumération et une fonction qui renvoie justement une de ces valeurs énumérées en fonction du caractère que l'on a rencontré, ce n'est pas juste pour m'amuser ni pour faire joli...

    C'est parce que les termes "addition", "soustraction", "multication" ou "division" sont beaucoup plus explicite que les valeurs numériques qui y sont associées.

    Pourquoi ne pas avoir utilisé ces termes pour les tests que l'on retrouves aux lignes 151, 176 et 200 ce serait beaucoup plus clair pour la personne qui lirait le code, et le compilateur comprendrait tout aussi bien

    La cerise sur le gâteau étant que, si un jour, je décidais de mettre les termes dans un ordre différent ou même d'associer des valeurs numériques spécifiques à ces termes (par exemple 1 pour addition, 2 pour soustraction, 4 pour multiplication et 8 pour division), je n'aurais même plus besoin de modifier le code ailleurs

    En conclusion

    Toutes ces remarques devraient déjà te permettre d'améliorer énormément ton code, et même, de manière indirecte, de le rendre "plus générique", quel que soit le sens que tu donnes à cette expression.

    J'aurais pu entrer dans énormément de détails supplémentaires, mais, au vu de la longueur de cette réponse, j'aurais couru le risque que le système la refuse une fois de plus car "elle contient plus de caractères qu'on ne peut en mettre en base de donnée".

    N'hésites pas à demander des précisions sur les points que j'aurais décidément trop effleurés
    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

  12. #12
    Expert éminent sénior
    Avatar de Mat.M
    Profil pro
    Développeur informatique
    Inscrit en
    Novembre 2006
    Messages
    8 352
    Détails du profil
    Informations personnelles :
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Novembre 2006
    Messages : 8 352
    Points : 20 359
    Points
    20 359
    Par défaut
    Citation Envoyé par binco Voir le message
    Comment faire pour calculer l'opération?
    Peut-être qu'un "parser" d'expression serait utile ?
    En voilà un excellent que j'utilise sur mon projet source sur GitHub

  13. #13
    Membre habitué
    Homme Profil pro
    sans emploi
    Inscrit en
    Février 2014
    Messages
    365
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Maine et Loire (Pays de la Loire)

    Informations professionnelles :
    Activité : sans emploi
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2014
    Messages : 365
    Points : 131
    Points
    131
    Par défaut
    merci beaucoup ça fonctionne

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 1
    Dernier message: 09/09/2011, 17h38
  2. Réponses: 6
    Dernier message: 14/02/2007, 22h08
  3. Des " dans une variable de type String
    Par 4lkaline dans le forum Langage
    Réponses: 6
    Dernier message: 06/11/2006, 15h20
  4. convertir une variable de type String en Number
    Par lilbrother974 dans le forum Flash
    Réponses: 13
    Dernier message: 06/09/2006, 09h28
  5. Ajouter a une variable de type string, un entier
    Par Little-Freud dans le forum SL & STL
    Réponses: 12
    Dernier message: 05/03/2005, 20h33

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