Résultat d'une expression mathématique a partir d'un string

Résolu/Fermé
LittDev - Modifié par LittDev le 12/06/2016 à 06:41
mamiemando Messages postés 33079 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 23 avril 2024 - 25 juin 2016 à 10:33
Bonjour,

J'ai crée un programme qui tire d'un string une expression mathématique en utilisant la notation polonaise inverse, puis qui calcule le résultat de l'expression, je voudrais savoir si y'a des énormités dans le code, ou sinon s'il y a quelques trucs à améliorer

https://fr.wikipedia.org/wiki/Notation_polonaise_inverse

Merci


#include <iostream>
#include <string>
#include <stack>
#include <list>
#include <sstream>
#include <cmath>

using namespace std;

class Token
{
public:
    Token() : data("") {}
    Token(string const& m_data) : data(m_data) {}
    Token(Token const& m_Token) : data(m_Token.data) {}
    ~Token() {}

    bool is_number()
    {
        istringstream iss(data);
        double temp;
        return ( iss >> temp ) && ( iss.eof() );
    }

    double numeric_value()
    {
        istringstream iss(data);
        double value;
        iss >> value;
        return value;
    }

    void show(ostream &flux) const
    {
        flux << data;
    }
    Token& operator+=(char const& input)
    {
        data+=input;
        return *this;
    }
    Token& operator=(string const& input)
    {
        data=input;
        return *this;
    }
    string get_data()
    {
        return data;
    }
    void set_data(string const& input)
    {
        data=input;
    }

protected:
    string data;
};

ostream &operator<<(ostream &flux, Token const& data)
{
    data.show(flux);
    return flux;
}

bool operator|=(char op, string operators)
{
    int i(0);

    while(operators[i]!='\0')
    {
        if(operators[i]==op)
            return true;
        i++;
    }
    return false;
}

bool operator>=(char op, string operators)
{
    int i(0);

    while(operators[i]!='\0')
    {
        if(operators[i]==op)
            return false;
        i++;
    }
    return true;
}

bool operator|=(Token input, string operators)
{
    int i(0), j(0);

    while(i<operators.length())
    {
        if(input.get_data().at(j)==operators.at(i))
            j++;
        else
            j=0;

        if(j==input.get_data().length())
            return true;
        i++;
    }

    return false;
}

bool priority(Token token_operator, string stack_operator)
{
    int i(0), j(0);
    string operators("+-*/^");

    while(operators.at(i)!=token_operator.get_data().at(0))
        i++;

    while(operators.at(j)!=stack_operator.at(0))
        j++;

    if(j/2 > i/2)
        return true;
    else
        return false;
}

void show_list_extended(list <Token> input, stack <Token> operators_stack, Token token)
{
    cout << "Token : " << token << endl << "Stack : ";

    while(!operators_stack.empty())
    {
        cout << operators_stack.top();
        operators_stack.pop();
    }
    cout << endl << "Output : ";

    for(list <Token> ::iterator iter=input.begin();iter!=input.end();iter++)
        cout << *iter << " ";
    cout << endl;
    cout << endl;
}

void show_list(list <Token> input)
{
    for(list <Token> ::iterator iter=input.begin();iter!=input.end();iter++)
        cout << *iter << " ";
    cout << endl;
}

string remove_invalid_chars(string input)
{
    int i(0);
    string valid_chars("0123456789.+-*/^()");

    while(input[i]!='\0')
    {
        while(input[i] >= valid_chars && input[i]!='\0')
        {
            input.replace(i,1,"");
        }
        i++;
    }

    return input;
}

bool count_brackets(string input)
{
    int nb_brackets_left(0), nb_brackets_right(0), i(0);

    while(i<input.length())
    {
        if(input.at(i)=='(')
            nb_brackets_left++;
        else if(input.at(i)==')')
            nb_brackets_right++;
        i++;
    }
    if(nb_brackets_left==nb_brackets_right)
        return true;
    return false;
}

bool validity(string *input)
{
    string operators("+-*/^");

    *input=remove_invalid_chars(*input);

    if(!count_brackets(*input))
        return false;

    int cpt(0), i(0);

    if( ((*input).at(i) |= operators) || ((*input).at((*input).length()-1) |= operators) )
        return false;

    while(i<(*input).length())
    {
        if((*input).at(i) |= operators)
        {
            cpt++;
            if(cpt>1)
                return false;
        }
        else
            cpt=0;
        i++;
    }
    return true;
}

list <Token> pack_to_list(string input)
{
    list <Token> output;
    string nbrs("0123456789."), op("+-");
    Token temp("");

    int i(0);

    while(input[i]!='\0')
    {
        temp="";

        if(i>0 && (input[i] |= op) && input[i-1]=='(' )
        {
            temp+=input[i];
            i++;
            while(input[i] |= nbrs)
            {
                temp+=input[i];
                i++;
            }
        }

        else if(input[i]|=nbrs)
            while(input[i]|=nbrs)
            {
                temp+=input[i];
                i++;
            }

        else
        {
            temp+=input[i];
            i++;
        }
        output.push_back(temp);
    }

    return output;
}

list <Token> tokenize_string(string input)
{
    string operators("+-*/^");
    stack <Token> operators_stack;
    list <Token> input_list;
    list <Token> output_list;
    int i(0);

    cout << input << endl << endl;
    input_list=pack_to_list(input);

    for(list <Token> ::iterator it=input_list.begin();it!=input_list.end();it++)
    {
        if(*it |= operators)
        {
            if(operators_stack.empty())
                operators_stack.push(*it);

            else if(operators_stack.top().get_data() == "(")
                operators_stack.push(*it);

            else if(operators_stack.top().get_data().at(0) |= operators)
            {
                if(priority(*it,operators_stack.top().get_data()))
                    while(priority(*it,operators_stack.top().get_data()))
                    {
                        output_list.push_back(operators_stack.top());
                        operators_stack.pop();
                    }
                operators_stack.push(*it);
            }
        }

        else if(*it |= "(")
            operators_stack.push(*it);

        else if(*it |= ")")
        {
            while(!operators_stack.empty())
            {
                if(operators_stack.top().get_data()=="(")
                {
                    operators_stack.pop();
                    break;
                }
                else
                {
                    output_list.push_back(operators_stack.top());
                    operators_stack.pop();
                }
            }
        }

        else
            output_list.push_back(*it);
        show_list_extended(output_list,operators_stack,*it);
    }

    while(!operators_stack.empty())
    {
        output_list.push_back(operators_stack.top());
        operators_stack.pop();
    }

    return output_list;
}

Token calculate_value(list <Token> input)
{
    list <Token> ::iterator it;
    double temp;
    string result;
    ostringstream stream;

    it=input.begin();
    Token nb_1(*it);
    it++;
    Token nb_2(*it);
    it++;
    Token op(*it);

    if(nb_1.is_number() && nb_2.is_number())
    {
        if(op.get_data()=="+")
        {
            temp=nb_1.numeric_value()+nb_2.numeric_value();
            stream << temp;
            return stream.str();
        }
        else if(op.get_data()=="-")
        {
            temp=nb_1.numeric_value()-nb_2.numeric_value();
            stream << temp;
            Token result(stream.str());
            return result;
        }
        else if(op.get_data()=="*")
        {
            temp=nb_1.numeric_value()*nb_2.numeric_value();
            stream << temp;
            Token result(stream.str());
            return result;
        }
        else if(op.get_data()=="/")
        {
            temp=nb_1.numeric_value()/nb_2.numeric_value();
            stream << temp;
            Token result(stream.str());
            return result;
        }
        else if(op.get_data()=="^")
        {
            temp=pow(nb_1.numeric_value(),nb_2.numeric_value());
            stream << temp;
            Token result(stream.str());
            return result;
        }
    }
}

double calculate_result(list <Token> input)
{
    Token temp;
    string operators("+-*/^");
    list <Token> new_input;

    for(list <Token> ::iterator iter_1=input.begin();iter_1!=input.end();iter_1++)
    {
        temp=*iter_1;

        if(temp.get_data() |= operators)
        {
            list <Token> elementar;
            for(int i(0);i<2;i++)
            {
                elementar.push_front(new_input.back());
                new_input.pop_back();
            }
            elementar.push_back(*iter_1);

            temp=calculate_value(elementar);
            new_input.push_back(temp);
        }
        else
            new_input.push_back(*iter_1);

    }
    show_list(new_input);
        cout << endl;
}

int main(void)
{

    list <Token> exp;
    string input;

    cout << "Entrer une expression algebrique ( ex : 1+3*7 ) : " << endl;

    getline(cin,input);

    while(!validity(&input))
    {
        cout << "Expression invalide, recommencez (pour les nbrs negatifs, utilisez les parantheses) : " << endl;
        getline(cin,input);
    }

    exp = tokenize_string(input);
    show_list(exp);
    cout << "=";
    calculate_result(exp);

    cout << endl << "cliquez sur une touche pour quitter ";
    getwchar();

    return 0;
}

1 réponse

mamiemando Messages postés 33079 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 23 avril 2024 7 749
Modifié par mamiemando le 21/06/2016 à 11:42
A première vue ça semble tenir la route mais quelques remarques :

1) il y a beaucoup de warning que tu pourrais fixer (
g++ -W -Wall plop.cpp
) :

plop.cpp: In function ‘bool operator|=(Token, std::__cxx11::string)’:
plop.cpp:96:9: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
while(i<operators.length())
^
plop.cpp:103:7: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
if(j==input.get_data().length())
^
plop.cpp: In function ‘bool count_brackets(std::__cxx11::string)’:
plop.cpp:173:9: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
while(i<input.length())
^
plop.cpp: In function ‘bool validity(std::__cxx11::string*)’:
plop.cpp:200:9: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
while(i<(*input).length())
^
plop.cpp: In function ‘std::__cxx11::list<Token> tokenize_string(std::__cxx11::string)’:
plop.cpp:262:6: warning: unused variable ‘i’ [-Wunused-variable]
int i(0);
^
plop.cpp: In function ‘double calculate_result(std::__cxx11::list<Token>)’:
plop.cpp:405:1: warning: no return statement in function returning non-void [-Wreturn-type]
}
^
plop.cpp: In function ‘Token calculate_value(std::__cxx11::list<Token>)’:
plop.cpp:374:1: warning: control reaches end of non-void function [-Wreturn-type]
}


2) tu pourrais définir tes opérateurs (
|=
...) sur des const char * et incrémenter le pointeur au fur et à mesure de la lecture.

const char * pc = "toto";
while(*pc++ != '+') {
  //...
}


Voici comment les traiter :
a) Pour les 4 premiers warnings :
i
devrait être un
std::size_t
.
b) Pour le 5e : supprimer i
c)
calculate_result
devrait retourner un
void
.
d)
calculate_value
devrait retourner quelque chose à la dernière ligne ou lever une exception (
throw std::runtime_error("zut!")
);

3) les
at(i)
sont des plus lents que
[i]
car ils contrôlent si tu es toujours dans le tableau. Et idéalement comme dit dans (1) autant travailler directement avec des pointeurs. Plus généralement il y a beaucoup d'endroits dans ton code qui marcheraient avec des
const char *
et donc a fortiori sur une std::string (quitte à manipuler
s.str()
).

4) généralement
x |= y
sert à définir
x = x | y
, du coup le rôle et le prototype de ces opérateurs ne sont pas très naturels. Personnellement j'aurais juste fait une fonction
read_until
ou un truc dans le genre.

5) idéalement (mais le forum ne s'y prête pas) il serait bien de faire un header par classe (genre token.hpp) et le fichier source correspondant (token.cpp). Attention à ne pas faire de
using namespace std;
dans les headers.

6) beaucoup plus grave : tes fonctions font toutes des passages de paramètre par recopie, alors que ce devrait être des passages par référence (idéalement en précisant
const
quand c'est nécessaire). Exemple :

void increment_list(std::list<int> & l) {
  std::list<int>::iterator lit(l.begin()), lend(l.end());
  for (; lit != lend; ++lit) {
    (*lit)++;
  }
}

void write_list(std::ostream & os, const std::list<int> & l) {
  std::list<int>::const_iterator lit(l.begin()), lend(l.end());
  for (; lit != lend; ++lit) {
    os << *lit << ' ';
  }
}


Bonne chance
0
Bonjour

Justement j'ai eu beaucoup de problème de comptabilité en string et char, mais c'est réglé maintenant

Merci beaucoup
0
mamiemando Messages postés 33079 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 23 avril 2024 7 749
Modifié par mamiemando le 25/06/2016 à 10:34
De rien bonne continuation !
0