Vous ne trouvez pas de réponse à votre problème ? Alors posez la question dans le forum. Souvenez-vous qu'il n'y a jamais de question bête, mais rester dans l'ignorance parce que l'on n'ose pas poser une question, ça c'est une erreur !

CALCUL : ANALYSE D'EXPRESSIONS ALGÉBRIQUES GRÂCE À LA RÉCURSIVITÉ (UTILISATION DES TEMPLATE ÉGALEMENT).


Information sur la source

Catégorie :Maths & Algorithmes Classé sous : calcul, analyse, expression, algèbre, math Niveau : Initié Date de création : 15/09/2007 Date de mise à jour : 12/10/2007 19:32:44 Vu / téléchargé: 4 587 / 147

Note :
Aucune note

Commentaire sur cette source (6)
Ajouter un commentaire et/ou une note

Description

Voici un petit analyseur d'expressions algébriques. Il gère les opérateurs +-*/, la priorité, et les parenthèses imbriquées.
Il y a déjà pas mal de code de ce genre, et sûrement des meilleurs que le mien, mais j'ai pensé qu'il était intéressant à cause de sa méthode particulière de résolution des expressions. Je me suis fortement inspiré d'un petit prog Java écrit comme exemple pour un livre (Introduction à Java, ou Learning Java, par Pat Niemeyer & Jonathan Knudsen, Oreilly, exemple: Calculator.java) qui utilise un code particulièrement élégant, utilisant toute la puissance de la récursivité pour éviter les codes avec 15 000 if à la suite, ce qui est le cas de la plupart des codes d'analyse d'expression postés ici.

(ACTUELLEMENT)-A ce jour, la gestion des opérateurs : */+-%^ est implémentée. Les parenthèses imbriquées sont supportées. On peut définir ses propres variables et fonctions pour les insérer dans des expressions (attention : précéder les noms de fonctions de _ dans les expressions analysées). Syntaxe de calculatrice (supporte les multiplications du genre (2ab+5)c + 3(2x^3) ).

A noter que les racine (carrée, cubique, etc...) sont supportées : il suffit de mettre un nombre à la puissance 1/2 pour la racine carrée, 1/3 pour la cubique, etc...
Par exemple, racine carrée de deux : 2^(1/2) ; racine cubique de deux : 2^(1/3)


J'ai commenté au maximum les fonctions intéressantes, mais si vous avez des questions, n'hésitez pas.

Note: j'utilise ici boost mais seulement pour la fonction erase_all. Si vous voulez compiler le code sans boost, il serait facile d'écrire votre propre fonction qui retire tout les espaces de l'expression.
 

Source

  • //////////////////////////////////////////////////////////////
  • //author lovejava (alias lovecpp, real name : Yann Hamdaoui)//
  • //////////////////////////////////////////////////////////////
  • //This source code is under the GNU/GPL license. //
  • //////////////////////////////////////////////////////////////
  • #ifndef _CALCUL_HPP_
  • #define _CALCUL_HPP_
  • #include <iostream>
  • #include <map>
  • #include <boost/algorithm/string.hpp>
  • #include <string>
  • #include <sstream>
  • #include <cmath>
  • using namespace std;
  • /**********************************************
  • *définit une fonctions (et ses sous fonctions)
  • *capable d'évaluer une expression mathématique sous forme de chaîne
  • *et de renvoyer le résultat
  • */
  • namespace calcul
  • {
  • /**
  • *exception signalant que la syntaxe de l'expression passée en paramètre
  • *n'est pas valide
  • */
  • class syntax_exception : public exception
  • {
  • protected:
  • string message;
  • int position; //position du caractère ayant provoqué l'erreur
  • //expression mathématique complète
  • string chaine;
  • public:
  • syntax_exception() throw() { };
  • syntax_exception(const string& message, const string& chaine = "", int position = string::npos) throw();
  • syntax_exception(const syntax_exception& source) throw();
  • const char* what() const throw();
  • syntax_exception& operator=(const syntax_exception& source) throw();
  • virtual ~syntax_exception() throw() { };
  • };
  • /**
  • *exception levée lors d'une division par zéro
  • */
  • class division_by_zero_exception : public exception
  • {
  • public:
  • division_by_zero_exception() throw() { };
  • division_by_zero_exception(const division_by_zero_exception& source) throw() { };
  • const char* what() const throw();
  • division_by_zero_exception& operator=(const division_by_zero_exception& source) throw() { };
  • virtual ~division_by_zero_exception() throw() { };
  • };
  • /**
  • *exception levée lorsqu'une variable ou une fonction utilisée dans un calcul n'est pas définie
  • */
  • class undefined_symbol_exception : public exception
  • {
  • protected:
  • string symbole; //le nom de la variable/fonction indéfinie
  • public:
  • undefined_symbol_exception(const string& symbole = "?") throw();
  • undefined_symbol_exception(const undefined_symbol_exception& source) throw();
  • const char* what() const throw();
  • undefined_symbol_exception& operator=(const undefined_symbol_exception& source) throw();
  • virtual ~undefined_symbol_exception() throw() { };
  • };
  • /**
  • *Classe qui permet d'envelopper les exceptions qui ont sont levées lorsque l'évaluateur
  • *parse une expression de fonction. Par exemple, si l'utilisateur définit la fonction f : (x^2 + 2x + 5)/(x-1)
  • *et qu'il tape 2*_f(1)/2, il peut ne pas comprendre d'où vient l'erreur si le seul message qu'il obtient est "division by zero".
  • *C'est encore plus un problème lorsqu'une erreur de syntaxe est présente dans la déclaration de la fonction.
  • *Cette exception permet donc de palier à ce problèmes en encapsulant l'erreur de base et en rajoutant dans le message d'erreur final
  • *la provenance de l'erreur : par exemple [in function f]division by zero
  • */
  • class fonction_error_wrapper_exception : public exception
  • {
  • protected:
  • string nom; //le nom de la fonction d'où provient l'erreur
  • string message_origine; //le message d'erreur d'origine
  • public:
  • fonction_error_wrapper_exception(const exception& erreur, const string& nom = "?") throw();
  • fonction_error_wrapper_exception(const fonction_error_wrapper_exception& source) throw();
  • const char* what() const throw();
  • fonction_error_wrapper_exception& operator=(const fonction_error_wrapper_exception& source) throw();
  • virtual ~fonction_error_wrapper_exception() throw() { };
  • };
  • #define ALGORITHMIC_FUNCTION number_type (*)(number_type)
  • /**
  • *Classe qui évalue une expression pour la parser sous forme de nombre.
  • *le paramètre template number_type permet de l'utiliser aussi bien avec
  • *des int, des doubles, des float, etc.
  • *le constructeur seul ne fait qu'initialiser l'objet, le parsage de l'expression
  • *se fait lors de la conversion en objet de type number_type (voir operator number_type())
  • *ou de l'appel explicite à eval()
  • */
  • template<typename number_type, typename algorithmic_function = number_type (*)(number_type)>
  • class expression_evaluator
  • {
  • /*public:
  • typedef (number_type (*)(number_type)) algorithmic_function;*/
  • protected:
  • string expression; //l'expression à parser
  • unsigned int index; //index utilisé lors du parsage
  • map<char, number_type> variables; //variables pouvant être utilisées pour des expressions
  • map<string, expression_evaluator<number_type> > fonctions; //fonctions définies par l'utilisateur
  • map<string, algorithmic_function> fonctions_algo; //fonctions algorithmiques et prédéfinies
  • //méthodes d'analyse et de parcours de la chaîne (voir le corps des méthodes
  • //pour des commentaires plus détaillés)
  • number_type somme(); //+ et -
  • number_type prior(); //*, / et %
  • number_type puissance(); // ^
  • number_type terme(); //analyse les nombres à proprement dit
  • bool more(); //si l'on est arrivé à la fin de la chaîne
  • /*Fonction qui renvoie true s'il y a un signe multiplié implicite à la position courante,
  • *comme par exemple dans 2x + 3ab(2 + 3z)d, c'est à dire lorsque des lettres, des nombres et/ou des parenthèses
  • *se suivent sans opérateur*/
  • bool implicit_mult();
  • public:
  • expression_evaluator() { };
  • expression_evaluator(const string& expression);
  • expression_evaluator(const expression_evaluator& source);
  • string get_expression() const;
  • /*cette fonction renvoie la valeur d'une variable, ou 0 si elle n'existe pas
  • *pour vérifier qu'une variable existe, utiliser is_var_defined() au préalable, car une variable
  • *existante peut très bien avoir pour valeur 0
  • */
  • number_type get_var(char variable) const;
  • /*
  • *renvoie l'expression d'une fonction définie par l'utilisateur
  • */
  • string get_function(const string& nom) const;
  • /*
  • *renvoie un pointeur de fonction vers une fonction "algorithmique" définie par l'utilisateur
  • *ou prédéfinie (sinus, cosinus, etc...). Par algorithmique, j'entend une fonction qui ne peut être
  • *définie seulement par une expression mathématique comme '2x+3', par exemple la fonction cosinus, partie entière
  • *ou encore racine carrée.
  • */
  • algorithmic_function get_algorithmic_function(const string& nom) const;
  • void set_expression(const string& expression);
  • //permet d'assigner une valeur à une variable
  • void set_var(char variable, number_type valeur);
  • //permet d'assigner à une fonction son expression
  • void set_function(const string& nom, const string& expression);
  • //permet d'assigner à une fonction son algorithme
  • void set_algorithmic_function(const string& nom, algorithmic_function fonction);
  • bool is_var_defined(char variable) const;
  • bool is_function_defined(const string& nom) const;
  • //évalue l'expression et renvoie un nombre
  • number_type eval();
  • //vérifie la syntaxe. Lève une syntax_exception si celle-ci n'est pas valide
  • void check_syntax() const throw(syntax_exception);
  • //méthode de classification des caractères
  • static bool is_digit(char caractere);
  • static bool is_alpha(char caractere);
  • static bool is_alphanum(char caractere);
  • static bool is_operator(char caractere);
  • static bool is_forbidden(char caractere);
  • expression_evaluator& operator=(const expression_evaluator& source);
  • operator number_type();
  • ~expression_evaluator();
  • };
  • //fonctions d'adaptation permettant d'utiliser les fonctions usuelles comme cosinus, log, etc...
  • //avec n'importe quel type générique number_type en effectuant les conversions nécessaires :
  • template<typename number_type>
  • number_type _abs_adapter(number_type parametre)
  • {
  • return (number_type)fabs((double)parametre);
  • }
  • template<typename number_type>
  • number_type _acos_adapter(number_type parametre)
  • {
  • return (number_type)acos((double)parametre);
  • }
  • template<typename number_type>
  • number_type _asin_adapter(number_type parametre)
  • {
  • return (number_type)asin((double)parametre);
  • }
  • template<typename number_type>
  • number_type _atan_adapter(number_type parametre)
  • {
  • return (number_type)atan((double)parametre);
  • }
  • template<typename number_type>
  • number_type _ceil_adapter(number_type parametre)
  • {
  • return (number_type)ceil((double)parametre);
  • }
  • template<typename number_type>
  • number_type _exp_adapter(number_type parametre)
  • {
  • return (number_type)exp((double)parametre);
  • }
  • template<typename number_type>
  • number_type _floor_adapter(number_type parametre)
  • {
  • return (number_type)floor((double)parametre);
  • }
  • template<typename number_type>
  • number_type _log_adapter(number_type parametre)
  • {
  • return (number_type)log((double)parametre);
  • }
  • template<typename number_type>
  • number_type _log10_adapter(number_type parametre)
  • {
  • return (number_type)log10((double)parametre);
  • }
  • template<typename number_type>
  • number_type _cos_adapter(number_type parametre)
  • {
  • return (number_type)cos((double)parametre);
  • }
  • template<typename number_type>
  • number_type _cosh_adapter(number_type parametre)
  • {
  • return (number_type)cosh((double)parametre);
  • }
  • template<typename number_type>
  • number_type _sin_adapter(number_type parametre)
  • {
  • return (number_type)sin((double)parametre);
  • }
  • template<typename number_type>
  • number_type _sinh_adapter(number_type parametre)
  • {
  • return (number_type)sinh((double)parametre);
  • }
  • template<typename number_type>
  • number_type _tan_adapter(number_type parametre)
  • {
  • return (number_type)tan((double)parametre);
  • }
  • template<typename number_type>
  • number_type _tanh_adapter(number_type parametre)
  • {
  • return (number_type)tanh((double)parametre);
  • }
  • ///////////////////////////////////////////////////////
  • syntax_exception::syntax_exception(const string& message, const string& chaine, int position) throw() : exception()
  • {
  • this->message = message;
  • this->chaine = chaine;
  • this->position = position;
  • }
  • syntax_exception::syntax_exception(const syntax_exception& source) throw() : exception()
  • {
  • *this = source;
  • }
  • const char* syntax_exception::what() const throw()
  • {
  • string temp(message);
  • //si un numéro de caractère est spécifié pour l'erreur syntaxique, on affiche le caractère en question
  • //et sa position dans l'expression après le message d'erreur
  • if(position != string::npos)
  • {
  • ostringstream sortie;
  • sortie << position+1;
  • temp.append("(caractère \'").append(chaine, position, 1).append("\', en position ").append(sortie.str()).append(")");
  • }
  • return temp.c_str();
  • }
  • syntax_exception& syntax_exception::operator=(const syntax_exception& source) throw()
  • {
  • message = source.message;
  • chaine = source.chaine;
  • position = source.position;
  • }
  • ///////////////////////////////////////////////////////////////////////////
  • const char* division_by_zero_exception::what() const throw()
  • {
  • return "division par zéro";
  • }
  • ///////////////////////////////////////////////////////////////////////////
  • undefined_symbol_exception::undefined_symbol_exception(const string& symbole) throw()
  • {
  • this->symbole = symbole;
  • }
  • undefined_symbol_exception::undefined_symbol_exception(const undefined_symbol_exception& source) throw()
  • {
  • *this = source;
  • }
  • const char* undefined_symbol_exception::what() const throw()
  • {
  • return string("\'").append(symbole).append("\' n'est pas défini.").c_str();
  • }
  • undefined_symbol_exception& undefined_symbol_exception::operator=(const undefined_symbol_exception& source) throw()
  • {
  • symbole = source.symbole;
  • }
  • ///////////////////////////////////////////////////////////////////////////
  • fonction_error_wrapper_exception::fonction_error_wrapper_exception(const exception& erreur, const string& nom) throw()
  • {
  • this->message_origine = erreur.what();
  • this->nom = nom;
  • }
  • fonction_error_wrapper_exception::fonction_error_wrapper_exception(const fonction_error_wrapper_exception& source) throw()
  • {
  • *this = source;
  • }
  • const char* fonction_error_wrapper_exception::what() const throw()
  • {
  • return string("[in function ").append(nom).append("]").append(message_origine).c_str();
  • }
  • fonction_error_wrapper_exception& fonction_error_wrapper_exception::operator=(const fonction_error_wrapper_exception& source) throw()
  • {
  • nom = source.nom;
  • message_origine = source.message_origine;
  • }
  • ///////////////////////////////////////////////////////////////////////////
  • template<typename number_type, typename algorithmic_function>
  • expression_evaluator<number_type, algorithmic_function>::expression_evaluator(const string& expression)
  • {
  • this->expression = expression;
  • //ajout des fonctions mathématiques de base
  • fonctions_algo["abs"] = &_abs_adapter<number_type>;
  • fonctions_algo["acos"] = &_acos_adapter<number_type>;
  • fonctions_algo["asin"] = &_asin_adapter<number_type>;
  • fonctions_algo["atan"] = &_atan_adapter<number_type>;
  • fonctions_algo["ceil"] = &_ceil_adapter<number_type>;
  • fonctions_algo["exp"] = &_exp_adapter<number_type>;
  • fonctions_algo["floor"] = &_floor_adapter<number_type>;
  • fonctions_algo["log"] = &_log_adapter<number_type>;
  • fonctions_algo["log10"] = &_log10_adapter<number_type>;
  • fonctions_algo["cos"] = &_cos_adapter<number_type>;
  • fonctions_algo["cosh"] = &_cosh_adapter<number_type>;
  • fonctions_algo["sin"] = &_sin_adapter<number_type>;
  • fonctions_algo["sinh"] = &_sinh_adapter<number_type>;
  • fonctions_algo["tan"] = &_tan_adapter<number_type>;
  • fonctions_algo["tanh"] = &_tanh_adapter<number_type>;
  • }
  • template<typename number_type, typename algorithmic_function>
  • expression_evaluator<number_type, algorithmic_function>::expression_evaluator(const expression_evaluator<number_type, algorithmic_function>& source)
  • {
  • *this = source;
  • }
  • template<typename number_type, typename algorithmic_function>
  • number_type expression_evaluator<number_type, algorithmic_function>::somme()
  • {
  • /*on commence par séparer les termes via les signes + et -,
  • *ceux qui ont la plus faible priorité. On calculera d'abord
  • *ces termes indépendamment pour ensuite leur appliquer, en dernier lieu,
  • *l'opérateur + ou -.
  • */
  • //à l'appel de la fonction, on considère que le premier élément est forcément
  • //soit un nombre, soit une parenthèse : on appelle donc la méthode terme()
  • //via la méthode prior() pour récupérer ce premier nombre.
  • number_type retour = prior();
  • //une fois obtenu un nombre, on cherche un signe + ou -
  • //si on trouve, on appel les méthodes sous-jacente pour calculer
  • //l'ensemble de la valeur qui sera ajoutée ou retranchée au final
  • while(more() && (expression[index] == '+' || expression[index] == '-'))
  • {
  • ++index; //on avance d'un caractère
  • if(expression[index-1] == '+')
  • retour += prior();
  • else
  • retour -= prior();
  • }
  • /*si on arrive à la fin d'une parenthèse, on sort d'une sous-expression.
  • *On avance donc la position courante pour que, lorsque l'on rend la main à
  • *la fonction terme(), l'analyse de l'expression ne s'arrête pas prématurément
  • *à cause du fait que comme la parenthèse n'est ni un opérateur, ni un nombre,
  • *les fonctions rendraient la main une à une sans effectuer aucun traitement supplémentaire
  • *Lorsqu'on enlève cette ligne par exemple, (5+(5+5)*2)*2 font 15 (en fait, seulement les
  • *trois premiers 5 on été analysés et ajoutés) et non pas 50.
  • */
  • if(more() && expression[index] == ')')
  • ++index;
  • return retour;
  • }
  • template<typename number_type, typename algorithmic_function>
  • number_type expression_evaluator<number_type, algorithmic_function>::prior()
  • {
  • number_type retour = puissance(); //on obtient le premier nombre de l'expression via puissance()
  • /*une fois obtenu un nombre, on cherche un signe *, / ou %
  • *on continue jusqu'à que le signe suivant ne soit ni un nombre ni un * ni un / ni un %
  • *pour être sûr d'avoir effectué toutes les divisions/multiplications de l'expression
  • *ou sous-expression. Ainsi la priorité est respectée et on rend la main à la fonction somme
  • */
  • bool implicite = false; //on doit savoir si la multiplication était implicite ou non pour avancer ou pas index
  • while( more() && (expression[index] == '*' || expression[index] == '/' || expression[index] == '%' || (implicite = implicit_mult())) )
  • {
  • if(!implicite)
  • ++index;
  • if(expression[index-1] == '/')
  • {
  • number_type temp = puissance();
  • if(temp == 0)
  • throw division_by_zero_exception();
  • retour /= temp;
  • }
  • else if(expression[index-1] == '%')
  • retour = (number_type) ((long long int)retour % (long long int)puissance());
  • else
  • retour *= puissance();
  • /*on réinitialise implicite pour les prochains passages, car si expression[index] vaut '*' par exemple, l'instruction
  • *implicite = implicit_mult() ne sera pas exécutée ; donc si au précédent passage implicite valait true, il garderait cette valeur
  • *si on ne le remettait pas à false
  • */
  • implicite = false;
  • }
  • return retour;
  • }
  • template<typename number_type, typename algorithmic_function>
  • number_type expression_evaluator<number_type, algorithmic_function>::puissance()
  • {
  • /*fonction qui regarde si dans l'expression ou sous-expression en cours
  • *il n'y a pas de signe '^'. Elle est après la fonction prior() dans le chaînage
  • *des appels car l'opérateur d'élévation à la puissance est le plus prioritaire
  • */
  • number_type retour = terme();
  • while(more() && (expression[index] == '^'))
  • {
  • ++index;
  • retour = (number_type)pow((double)retour, (double)terme());
  • }
  • return retour;
  • }
  • template<typename number_type, typename algorithmic_function>
  • number_type expression_evaluator<number_type, algorithmic_function>::terme()
  • {
  • //c'est la fonction qui lit réellement les nombres
  • //si on tombe sur une parenthèse ouvrante, on ré-analyse
  • //cette sous-expression comme une nouvelle expression apparentière
  • if(expression[index] == '(')
  • {
  • ++index;
  • return somme();
  • }
  • else
  • {
  • number_type nombre;
  • unsigned int index_depart = index;
  • bool moins_unaire = false; //s'il y avait un moins unaire au début de l'expression
  • //gestion du moins unaire : si l'on rencontre un moins lorsque les fonctions
  • //appellantes veulent un nombre, on considère que c'est un moins unaire
  • //on met un booléen à true puis on avance l'index courant
  • if(expression[index] == '-')
  • {
  • moins_unaire = true;
  • ++index;
  • ++index_depart;
  • }
  • if(expression_evaluator<number_type, algorithmic_function>::is_operator(expression[index]))
  • throw syntax_exception("plusieurs opérateurs se suivent", expression, index);
  • if(expression[index] == '_')
  • {
  • //on avance la position courante jusqu'à la parenthèse ouvrante
  • ++index;
  • ++index_depart;
  • for(; more() && expression[index] != '('; ++index) ;
  • //on doit récupérer la valeur d'une fonction
  • if(!more())
  • throw syntax_exception("Un nom de fonction doit être suivi d'une parenthèse ouvrante", expression, index_depart);
  • string nom_fonction = expression.substr(index_depart, index-index_depart);
  • typename map<string, /*expression_evaluator<number_type, algorithmic_function>::algorithmic_function*/ALGORITHMIC_FUNCTION>::iterator it1 = fonctions_algo.find(nom_fonction);
  • typename map<string, expression_evaluator<number_type, algorithmic_function> >::iterator it2 = fonctions.find(nom_fonction);
  • ++index; //on passe après la parenthèse
  • number_type x_value = somme(); //on évalue indépendamment l'expression qui suit la fonction (c'est à dire la valeur de x)
  • if(it1 != fonctions_algo.end())
  • {
  • try {
  • nombre = (*(*it1).second)(x_value);
  • }
  • //il faut convertir les messages d'erreurs pour bien spécifier que l'erreur se trouve dans la définition de la fonction,
  • //et non pas dans la chaîne analysée en ce moment
  • catch(const exception& e)
  • {
  • throw fonction_error_wrapper_exception(e, nom_fonction);
  • }
  • }
  • else if(it2 != fonctions.end())
  • {
  • //on met à jour les définitions de variables et de fonctions pour que la suite tout ça etc.
  • fonctions[nom_fonction].variables = variables;
  • fonctions[nom_fonction].fonctions = fonctions;
  • fonctions[nom_fonction].set_var('x', x_value);
  • try {
  • nombre = fonctions[nom_fonction].eval();
  • }
  • //il faut convertir les messages d'erreurs pour bien spécifier que l'erreur se trouve dans la définition de la fonction,
  • //et non pas dans la chaîne analysée en ce moment
  • catch(const exception& e)
  • {
  • throw fonction_error_wrapper_exception(e, nom_fonction);
  • }
  • }
  • else
  • throw undefined_symbol_exception(nom_fonction);
  • }
  • else if(expression_evaluator<number_type, algorithmic_function>::is_alpha(expression[index]))
  • {
  • char variable = expression[index];
  • if(!is_var_defined(variable))
  • throw undefined_symbol_exception(string(&variable, 1));
  • number_type valeur = get_var(variable);
  • nombre = valeur;
  • ++index;
  • }
  • else
  • {
  • //sinon, on lit le nombre à la position courante
  • for(; more() && is_digit(expression[index]); ++index) ;
  • string nombre_str(expression, index_depart, index-index_depart);
  • istringstream entree(nombre_str);
  • entree >> nombre;
  • }
  • if(moins_unaire)
  • return -nombre;
  • else
  • return nombre;
  • }
  • }
  • template<typename number_type, typename algorithmic_function>
  • bool expression_evaluator<number_type, algorithmic_function>::more()
  • {
  • return index < expression.length();
  • }
  • template<typename number_type, typename algorithmic_function>
  • bool expression_evaluator<number_type, algorithmic_function>::implicit_mult()
  • {
  • /*Vérifie s'il n'y a pas de signe multiplier implicite
  • *avec des variables (comme par exemple 2a(3+2) ou encore (x+2)(x-3)bc
  • *vérifications : si un chiffre et une lettre se suivent (ou l'inverse)
  • * si deux lettres se suivent
  • * si un chiffre/une lettre suivent une parenthèse fermante (ou précèdent une parenthèse ouvrante)
  • * si une parenthèse fermante et une parenthèse ouvrante se suivent
  • */
  • return ( (expression_evaluator<number_type, algorithmic_function>::is_digit(expression[index-1]) && expression_evaluator<number_type, algorithmic_function>::is_alpha(expression[index])) ||
  • (expression_evaluator<number_type, algorithmic_function>::is_alpha(expression[index-1]) && expression_evaluator<number_type, algorithmic_function>::is_digit(expression[index])) ||
  • (expression_evaluator<number_type, algorithmic_function>::is_alpha(expression[index-1]) && expression_evaluator<number_type, algorithmic_function>::is_alpha(expression[index])) ||
  • (expression_evaluator<number_type, algorithmic_function>::is_alphanum(expression[index-1]) && expression[index] == '(') ||
  • (expression[index-1] == ')' && expression_evaluator<number_type, algorithmic_function>::is_alphanum(expression[index])) ||
  • (expression[index-1] == ')' && expression[index] == '(') );
  • }
  • template<typename number_type, typename algorithmic_function>
  • bool expression_evaluator<number_type, algorithmic_function>::is_digit(char caractere)
  • {
  • return (caractere == '.' ||
  • (caractere >= '0' && caractere <= '9'));
  • }
  • template<typename number_type, typename algorithmic_function>
  • bool expression_evaluator<number_type, algorithmic_function>::is_alpha(char caractere)
  • {
  • return (caractere >= 'a' && caractere <= 'z') || (caractere >= 'A' && caractere <= 'Z') || caractere == '_';
  • }
  • template<typename number_type, typename algorithmic_function>
  • bool expression_evaluator<number_type, algorithmic_function>::is_alphanum(char caractere)
  • {
  • return is_digit(caractere) || is_alpha(caractere);
  • }
  • template<typename number_type, typename algorithmic_function>
  • bool expression_evaluator<number_type, algorithmic_function>::is_operator(char caractere)
  • {
  • return (caractere == '+' || caractere == '-' || caractere == '%'
  • || caractere == '*' || caractere == '/' || caractere == '^');
  • }
  • template<typename number_type, typename algorithmic_function>
  • bool expression_evaluator<number_type, algorithmic_function>::is_forbidden(char caractere)
  • {
  • return (!is_digit(caractere) && !is_operator(caractere) && !is_alpha(caractere)
  • && caractere != '(' && caractere != ')' && caractere != ' ');
  • }
  • template<typename number_type, typename algorithmic_function>
  • string expression_evaluator<number_type, algorithmic_function>::get_expression() const
  • {
  • return expression;
  • }
  • template<typename number_type, typename algorithmic_function>
  • number_type expression_evaluator<number_type, algorithmic_function>::get_var(char variable) const
  • {
  • typename map<char, number_type>::const_iterator it = variables.find(variable);
  • if(it == variables.end())
  • return 0;
  • else
  • return (*it).second;
  • }
  • template<typename number_type, typename algorithmic_function>
  • string expression_evaluator<number_type, algorithmic_function>::get_function(const string& nom) const
  • {
  • typename map<string, expression_evaluator<number_type, algorithmic_function> >::const_iterator it = fonctions.find(nom);
  • if(it == fonctions.end())
  • return "";
  • else
  • return (*it).second.get_expression();
  • }
  • template<typename number_type, typename algorithmic_function>
  • algorithmic_function expression_evaluator<number_type, algorithmic_function>::get_algorithmic_function(const string& nom) const
  • {
  • typename map<string, /*expression_evaluator<number_type, algorithmic_function>::algorithmic_function*/ALGORITHMIC_FUNCTION>::const_iterator it = fonctions.find(nom);
  • if(it == fonctions.end())
  • return NULL;
  • else
  • return (*it).second;
  • }
  • template<typename number_type, typename algorithmic_function>
  • void expression_evaluator<number_type, algorithmic_function>::set_expression(const string& expression)
  • {
  • this->expression = expression;
  • }
  • template<typename number_type, typename algorithmic_function>
  • void expression_evaluator<number_type, algorithmic_function>::set_var(char variable, number_type valeur)
  • {
  • variables[variable] = valeur;
  • }
  • template<typename number_type, typename algorithmic_function>
  • void expression_evaluator<number_type, algorithmic_function>::set_function(const string& nom, const string& expression)
  • {
  • //on créer une nouvelle fonction seulement si elle n'est pas déjà définie parmis les fonctions algorithmiques ou prédéfinies
  • //pour éviter les conflits
  • if(fonctions_algo.find(nom) == fonctions_algo.end())
  • fonctions[nom] = expression;
  • }
  • template<typename number_type, typename algorithmic_function>
  • void expression_evaluator<number_type, algorithmic_function>::set_algorithmic_function(const string& nom, algorithmic_function fonction)
  • {
  • //si la fonction existe déjà parmis les fonctions définies par l'utilisateur, on la supprime de la liste avant de l'insérer
  • //parmis les fonctions algorithmiques
  • typename map<string, expression_evaluator<number_type, algorithmic_function> >::iterator it = fonctions.find(nom);
  • if(it != fonctions.end())
  • fonctions.erase(it);
  • fonctions_algo[nom] = fonction;
  • }
  • template<typename number_type, typename algorithmic_function>
  • bool expression_evaluator<number_type, algorithmic_function>::is_var_defined(char variable) const
  • {
  • return variables.find(variable) != variables.end();
  • }
  • template<typename number_type, typename algorithmic_function>
  • bool expression_evaluator<number_type, algorithmic_function>::is_function_defined(const string& nom) const
  • {
  • return fonctions.find(nom) != fonctions.end() || fonctions_algo.find(nom) != fonctions_algo.end();
  • }
  • template<typename number_type, typename algorithmic_function>
  • number_type expression_evaluator<number_type, algorithmic_function>::eval()
  • {
  • if(expression.find(' ') != string::npos)
  • boost::algorithm::erase_all(expression, " "); //suppression des espaces
  • check_syntax();
  • index = 0;
  • return somme();
  • }
  • template<typename number_type, typename algorithmic_function>
  • void expression_evaluator<number_type, algorithmic_function>::check_syntax() const throw(syntax_exception)
  • {
  • if((expression_evaluator<number_type, algorithmic_function>::is_operator(expression[0]) && expression[0] != '-')
  • || expression_evaluator<number_type, algorithmic_function>::is_operator(expression[expression.length()-1]))
  • throw syntax_exception("l'expression commence ou se termine par un opérateur");
  • register int i;
  • register int compteur = 0;
  • for(i = 0; i < expression.length(); ++i)
  • {
  • if(expression_evaluator<number_type, algorithmic_function>::is_forbidden(expression[i]))
  • throw syntax_exception("caractère interdit", expression, i);
  • if(expression[i] == '(')
  • ++compteur;
  • if(expression[i] == ')')
  • --compteur;
  • }
  • if(compteur != 0)
  • throw syntax_exception("le nombre de parenthèses ouvrantes ne correspond pas au nombres de parenthèses fermantes.");
  • }
  • template<typename number_type, typename algorithmic_function>
  • expression_evaluator<number_type, algorithmic_function>& expression_evaluator<number_type, algorithmic_function>::operator=(const expression_evaluator<number_type, algorithmic_function>& source)
  • {
  • expression = source.expression;
  • variables = source.variables;
  • fonctions = source.fonctions;
  • fonctions_algo = source.fonctions_algo;
  • }
  • template<typename number_type, typename algorithmic_function>
  • expression_evaluator<number_type, algorithmic_function>::operator number_type()
  • {
  • return eval();
  • }
  • template<typename number_type, typename algorithmic_function>
  • expression_evaluator<number_type, algorithmic_function>::~expression_evaluator()
  • {
  • }
  • }
  • #endif /* ndef _CALCUL_HPP_ */
//////////////////////////////////////////////////////////////
//author lovejava (alias lovecpp, real name : Yann Hamdaoui)//
//////////////////////////////////////////////////////////////
//This source code is under the GNU/GPL license.            //
//////////////////////////////////////////////////////////////

#ifndef _CALCUL_HPP_
#define _CALCUL_HPP_

#include <iostream>
#include <map>
#include <boost/algorithm/string.hpp>
#include <string>
#include <sstream>
#include <cmath>

using namespace std;

/**********************************************
 *définit une fonctions (et ses sous fonctions)
 *capable d'évaluer une expression mathématique sous forme de chaîne
 *et de renvoyer le résultat
 */

namespace calcul
{
    /**
     *exception signalant que la syntaxe de l'expression passée en paramètre
     *n'est pas valide
     */
    class syntax_exception : public exception
    {
        protected:
            string message;
            int position; //position du caractère ayant provoqué l'erreur
            //expression mathématique complète
            string chaine;

        public:
            syntax_exception() throw() { };
            syntax_exception(const string& message, const string& chaine = "", int position = string::npos) throw();
            syntax_exception(const syntax_exception& source) throw();

            const char* what() const throw();

            syntax_exception& operator=(const syntax_exception& source) throw();

            virtual ~syntax_exception() throw() { };
    };

    /**
     *exception levée lors d'une division par zéro
     */
    class division_by_zero_exception : public exception
    {
        public:
            division_by_zero_exception() throw() { };
            division_by_zero_exception(const division_by_zero_exception& source) throw() { };

            const char* what() const throw();

            division_by_zero_exception& operator=(const division_by_zero_exception& source) throw() { };

            virtual ~division_by_zero_exception() throw() { };
    };

    /**
     *exception levée lorsqu'une variable ou une fonction utilisée dans un calcul n'est pas définie
     */
    class undefined_symbol_exception : public exception
    {
        protected:
            string symbole; //le nom de la variable/fonction indéfinie

        public:
            undefined_symbol_exception(const string& symbole = "?") throw();
            undefined_symbol_exception(const undefined_symbol_exception& source) throw();

            const char* what() const throw();

            undefined_symbol_exception& operator=(const undefined_symbol_exception& source) throw();

            virtual ~undefined_symbol_exception() throw() { };
    };

    /**
     *Classe qui permet d'envelopper les exceptions qui ont sont levées lorsque l'évaluateur
     *parse une expression de fonction. Par exemple, si l'utilisateur définit la fonction f : (x^2 + 2x + 5)/(x-1)
     *et qu'il tape 2*_f(1)/2, il peut ne pas comprendre d'où vient l'erreur si le seul message qu'il obtient est "division by zero".
     *C'est encore plus un problème lorsqu'une erreur de syntaxe est présente dans la déclaration de la fonction.
     *Cette exception permet donc de palier à ce problèmes en encapsulant l'erreur de base et en rajoutant dans le message d'erreur final
     *la provenance de l'erreur : par exemple [in function f]division by zero
     */
    class fonction_error_wrapper_exception : public exception
    {
        protected:
            string nom; //le nom de la fonction d'où provient l'erreur
            string message_origine; //le message d'erreur d'origine

        public:
            fonction_error_wrapper_exception(const exception& erreur, const string& nom = "?") throw();
            fonction_error_wrapper_exception(const fonction_error_wrapper_exception& source) throw();

            const char* what() const throw();

            fonction_error_wrapper_exception& operator=(const fonction_error_wrapper_exception& source) throw();

            virtual ~fonction_error_wrapper_exception() throw() { };
    };

    #define ALGORITHMIC_FUNCTION number_type (*)(number_type)

    /**
     *Classe qui évalue une expression pour la parser sous forme de nombre.
     *le paramètre template number_type permet de l'utiliser aussi bien avec
     *des int, des doubles, des float, etc.
     *le constructeur seul ne fait qu'initialiser l'objet, le parsage de l'expression
     *se fait lors de la conversion en objet de type number_type (voir operator number_type())
     *ou de l'appel explicite à eval()
     */
    template<typename number_type, typename algorithmic_function = number_type (*)(number_type)>
    class expression_evaluator
    {
        /*public:
            typedef (number_type (*)(number_type)) algorithmic_function;*/

        protected:
            string expression; //l'expression à parser
            unsigned int index; //index utilisé lors du parsage
            map<char, number_type> variables; //variables pouvant être utilisées pour des expressions
            map<string, expression_evaluator<number_type> > fonctions; //fonctions définies par l'utilisateur
            map<string, algorithmic_function> fonctions_algo; //fonctions algorithmiques et prédéfinies

            //méthodes d'analyse et de parcours de la chaîne (voir le corps des méthodes
            //pour des commentaires plus détaillés)
            number_type somme(); //+ et -
            number_type prior(); //*, / et %
            number_type puissance(); // ^
            number_type terme(); //analyse les nombres à proprement dit

            bool more(); //si l'on est arrivé à la fin de la chaîne
            /*Fonction qui renvoie true s'il y a un signe multiplié implicite à la position courante,
             *comme par exemple dans 2x + 3ab(2 + 3z)d, c'est à dire lorsque des lettres, des nombres et/ou des parenthèses
             *se suivent sans opérateur*/
            bool implicit_mult();

        public:
            expression_evaluator() { };
            expression_evaluator(const string& expression);
            expression_evaluator(const expression_evaluator& source);

            string get_expression() const;
            /*cette fonction renvoie la valeur d'une variable, ou 0 si elle n'existe pas
             *pour vérifier qu'une variable existe, utiliser is_var_defined() au préalable, car une variable
             *existante peut très bien avoir pour valeur 0
             */
            number_type get_var(char variable) const;
            /*
             *renvoie l'expression d'une fonction définie par l'utilisateur
             */
            string get_function(const string& nom) const;
            /*
             *renvoie un pointeur de fonction vers une fonction "algorithmique" définie par l'utilisateur
             *ou prédéfinie (sinus, cosinus, etc...). Par algorithmique, j'entend une fonction qui ne peut être
             *définie seulement par une expression mathématique comme '2x+3', par exemple la fonction cosinus, partie entière
             *ou encore racine carrée.
             */
            algorithmic_function get_algorithmic_function(const string& nom) const;

            void set_expression(const string& expression);
            //permet d'assigner une valeur à une variable
            void set_var(char variable, number_type valeur);
            //permet d'assigner à une fonction son expression
            void set_function(const string& nom, const string& expression);
            //permet d'assigner à une fonction son algorithme
            void set_algorithmic_function(const string& nom, algorithmic_function fonction);

            bool is_var_defined(char variable) const;
            bool is_function_defined(const string& nom) const;

            //évalue l'expression et renvoie un nombre
            number_type eval();
            //vérifie la syntaxe. Lève une syntax_exception si celle-ci n'est pas valide
            void check_syntax() const throw(syntax_exception);

            //méthode de classification des caractères
            static bool is_digit(char caractere);
            static bool is_alpha(char caractere);
            static bool is_alphanum(char caractere);
            static bool is_operator(char caractere);
            static bool is_forbidden(char caractere);

            expression_evaluator& operator=(const expression_evaluator& source);
            operator number_type();

            ~expression_evaluator();
    };

    //fonctions d'adaptation permettant d'utiliser les fonctions usuelles comme cosinus, log, etc...
    //avec n'importe quel type générique number_type en effectuant les conversions nécessaires :
    template<typename number_type>
    number_type _abs_adapter(number_type parametre)
    {
        return (number_type)fabs((double)parametre);
    }


    template<typename number_type>
    number_type _acos_adapter(number_type parametre)
    {
        return (number_type)acos((double)parametre);
    }

    template<typename number_type>
    number_type _asin_adapter(number_type parametre)
    {
        return (number_type)asin((double)parametre);
    }

    template<typename number_type>
    number_type _atan_adapter(number_type parametre)
    {
        return (number_type)atan((double)parametre);
    }


    template<typename number_type>
    number_type _ceil_adapter(number_type parametre)
    {
        return (number_type)ceil((double)parametre);
    }


    template<typename number_type>
    number_type _exp_adapter(number_type parametre)
    {
        return (number_type)exp((double)parametre);
    }


    template<typename number_type>
    number_type _floor_adapter(number_type parametre)
    {
        return (number_type)floor((double)parametre);
    }


    template<typename number_type>
    number_type _log_adapter(number_type parametre)
    {
        return (number_type)log((double)parametre);
    }

    template<typename number_type>
    number_type _log10_adapter(number_type parametre)
    {
        return (number_type)log10((double)parametre);
    }


    template<typename number_type>
    number_type _cos_adapter(number_type parametre)
    {
        return (number_type)cos((double)parametre);
    }

    template<typename number_type>
    number_type _cosh_adapter(number_type parametre)
    {
        return (number_type)cosh((double)parametre);
    }

    template<typename number_type>
    number_type _sin_adapter(number_type parametre)
    {
        return (number_type)sin((double)parametre);
    }

    template<typename number_type>
    number_type _sinh_adapter(number_type parametre)
    {
        return (number_type)sinh((double)parametre);
    }

    template<typename number_type>
    number_type _tan_adapter(number_type parametre)
    {
        return (number_type)tan((double)parametre);
    }

    template<typename number_type>
    number_type _tanh_adapter(number_type parametre)
    {
        return (number_type)tanh((double)parametre);
    }

    ///////////////////////////////////////////////////////

    syntax_exception::syntax_exception(const string& message, const string& chaine, int position) throw() : exception()
    {
        this->message = message;
        this->chaine = chaine;
        this->position = position;
    }

    syntax_exception::syntax_exception(const syntax_exception& source) throw() : exception()
    {
        *this = source;
    }

    const char* syntax_exception::what() const throw()
    {
        string temp(message);
        //si un numéro de caractère est spécifié pour l'erreur syntaxique, on affiche le caractère en question
        //et sa position dans l'expression après le message d'erreur
        if(position != string::npos)
        {
            ostringstream sortie;
            sortie << position+1;
            temp.append("(caractère \'").append(chaine, position, 1).append("\', en position ").append(sortie.str()).append(")");
        }

        return temp.c_str();
    }

    syntax_exception& syntax_exception::operator=(const syntax_exception& source) throw()
    {
        message = source.message;
        chaine = source.chaine;
        position = source.position;
    }

    ///////////////////////////////////////////////////////////////////////////

    const char* division_by_zero_exception::what() const throw()
    {
        return "division par zéro";
    }

    ///////////////////////////////////////////////////////////////////////////

    undefined_symbol_exception::undefined_symbol_exception(const string& symbole) throw()
    {
        this->symbole = symbole;
    }

    undefined_symbol_exception::undefined_symbol_exception(const undefined_symbol_exception& source) throw()
    {
        *this = source;
    }

    const char* undefined_symbol_exception::what() const throw()
    {
        return string("\'").append(symbole).append("\' n'est pas défini.").c_str();
    }

    undefined_symbol_exception& undefined_symbol_exception::operator=(const undefined_symbol_exception& source) throw()
    {
        symbole = source.symbole;
    }

    ///////////////////////////////////////////////////////////////////////////

    fonction_error_wrapper_exception::fonction_error_wrapper_exception(const exception& erreur, const string& nom) throw()
    {
        this->message_origine = erreur.what();
        this->nom = nom;
    }

    fonction_error_wrapper_exception::fonction_error_wrapper_exception(const fonction_error_wrapper_exception& source) throw()
    {
        *this = source;
    }

    const char* fonction_error_wrapper_exception::what() const throw()
    {
        return string("[in function ").append(nom).append("]").append(message_origine).c_str();
    }

    fonction_error_wrapper_exception& fonction_error_wrapper_exception::operator=(const fonction_error_wrapper_exception& source) throw()
    {
        nom = source.nom;
        message_origine = source.message_origine;
    }

    ///////////////////////////////////////////////////////////////////////////

    template<typename number_type, typename algorithmic_function>
    expression_evaluator<number_type, algorithmic_function>::expression_evaluator(const string& expression)
    {
        this->expression = expression;

        //ajout des fonctions mathématiques de base
        fonctions_algo["abs"] = &_abs_adapter<number_type>;

        fonctions_algo["acos"] = &_acos_adapter<number_type>;
        fonctions_algo["asin"] = &_asin_adapter<number_type>;
        fonctions_algo["atan"] = &_atan_adapter<number_type>;

        fonctions_algo["ceil"] = &_ceil_adapter<number_type>;

        fonctions_algo["exp"] = &_exp_adapter<number_type>;

        fonctions_algo["floor"] = &_floor_adapter<number_type>;

        fonctions_algo["log"] = &_log_adapter<number_type>;
        fonctions_algo["log10"] = &_log10_adapter<number_type>;

        fonctions_algo["cos"] = &_cos_adapter<number_type>;
        fonctions_algo["cosh"] = &_cosh_adapter<number_type>;
        fonctions_algo["sin"] = &_sin_adapter<number_type>;
        fonctions_algo["sinh"] = &_sinh_adapter<number_type>;
        fonctions_algo["tan"] = &_tan_adapter<number_type>;
        fonctions_algo["tanh"] = &_tanh_adapter<number_type>;
    }

    template<typename number_type, typename algorithmic_function>
    expression_evaluator<number_type, algorithmic_function>::expression_evaluator(const expression_evaluator<number_type, algorithmic_function>& source)
    {
        *this = source;
    }

    template<typename number_type, typename algorithmic_function>
    number_type expression_evaluator<number_type, algorithmic_function>::somme()
    {
        /*on commence par séparer les termes via les signes + et -,
         *ceux qui ont la plus faible priorité. On calculera d'abord
         *ces termes indépendamment pour ensuite leur appliquer, en dernier lieu,
         *l'opérateur + ou -.
         */


        //à l'appel de la fonction, on considère que le premier élément est forcément
        //soit un nombre, soit une parenthèse : on appelle donc la méthode terme()
        //via la méthode prior() pour récupérer ce premier nombre.
        number_type retour = prior();

        //une fois obtenu un nombre, on cherche un signe + ou -
        //si on trouve, on appel les méthodes sous-jacente pour calculer
        //l'ensemble de la valeur qui sera ajoutée ou retranchée au final
        while(more() && (expression[index] == '+' || expression[index] == '-'))
        {
            ++index; //on avance d'un caractère
            if(expression[index-1] == '+')
                retour += prior();
            else
                retour -= prior();
        }

        /*si on arrive à la fin d'une parenthèse, on sort d'une sous-expression.
         *On avance donc la position courante pour que, lorsque l'on rend la main à
         *la fonction terme(), l'analyse de l'expression ne s'arrête pas prématurément
         *à cause du fait que comme la parenthèse n'est ni un opérateur, ni un nombre,
         *les fonctions rendraient la main une à une sans effectuer aucun traitement supplémentaire
         *Lorsqu'on enlève cette ligne par exemple, (5+(5+5)*2)*2 font 15 (en fait, seulement les
         *trois premiers 5 on été analysés et ajoutés) et non pas 50.
         */
        if(more() && expression[index] == ')')
            ++index;

        return retour;
    }

    template<typename number_type, typename algorithmic_function>
    number_type expression_evaluator<number_type, algorithmic_function>::prior()
    {
        number_type retour = puissance(); //on obtient le premier nombre de l'expression via puissance()

        /*une fois obtenu un nombre, on cherche un signe *, / ou %
         *on continue jusqu'à que le signe suivant ne soit ni un nombre ni un * ni un / ni un %
         *pour être sûr d'avoir effectué toutes les divisions/multiplications de l'expression
         *ou sous-expression. Ainsi la priorité est respectée et on rend la main à la fonction somme
         */
        bool implicite = false; //on doit savoir si la multiplication était implicite ou non pour avancer ou pas index
        while( more() && (expression[index] == '*' || expression[index] == '/' || expression[index] == '%' || (implicite = implicit_mult())) )
        {
            if(!implicite)
                ++index;
            if(expression[index-1] == '/')
            {
                number_type temp = puissance();
                if(temp == 0)
                    throw division_by_zero_exception();

                retour /= temp;
            }
            else if(expression[index-1] == '%')
                retour = (number_type) ((long long int)retour % (long long int)puissance());
            else
                retour *= puissance();

            /*on réinitialise implicite pour les prochains passages, car si expression[index] vaut '*' par exemple, l'instruction
             *implicite = implicit_mult() ne sera pas exécutée ; donc si au précédent passage implicite valait true, il garderait cette valeur
             *si on ne le remettait pas à false
             */
            implicite = false;
        }

        return retour;
    }

    template<typename number_type, typename algorithmic_function>
    number_type expression_evaluator<number_type, algorithmic_function>::puissance()
    {
        /*fonction qui regarde si dans l'expression ou sous-expression en cours
         *il n'y a pas de signe '^'. Elle est après la fonction prior() dans le chaînage
         *des appels car l'opérateur d'élévation à la puissance est le plus prioritaire
         */

        number_type retour = terme();

        while(more() && (expression[index] == '^'))
        {
            ++index;
            retour = (number_type)pow((double)retour, (double)terme());
        }

        return retour;
    }

    template<typename number_type, typename algorithmic_function>
    number_type expression_evaluator<number_type, algorithmic_function>::terme()
    {
        //c'est la fonction qui lit réellement les nombres
        //si on tombe sur une parenthèse ouvrante, on ré-analyse
        //cette sous-expression comme une nouvelle expression apparentière
        if(expression[index] == '(')
        {
            ++index;
            return somme();
        }
        else
        {
            number_type nombre;
            unsigned int index_depart = index;
            bool moins_unaire = false; //s'il y avait un moins unaire au début de l'expression

            //gestion du moins unaire : si l'on rencontre un moins lorsque les fonctions
            //appellantes veulent un nombre, on considère que c'est un moins unaire
            //on met un booléen à true puis on avance l'index courant
            if(expression[index] == '-')
            {
                moins_unaire = true;
                ++index;
                ++index_depart;
            }

            if(expression_evaluator<number_type, algorithmic_function>::is_operator(expression[index]))
                throw syntax_exception("plusieurs opérateurs se suivent", expression, index);

            if(expression[index] == '_')
            {
                //on avance la position courante jusqu'à la parenthèse ouvrante
                ++index;
                ++index_depart;

                for(; more() && expression[index] != '('; ++index) ;
                //on doit récupérer la valeur d'une fonction
                if(!more())
                    throw syntax_exception("Un nom de fonction doit être suivi d'une parenthèse ouvrante", expression, index_depart);

                string nom_fonction = expression.substr(index_depart, index-index_depart);
                typename map<string, /*expression_evaluator<number_type, algorithmic_function>::algorithmic_function*/ALGORITHMIC_FUNCTION>::iterator it1 = fonctions_algo.find(nom_fonction);
                typename map<string, expression_evaluator<number_type, algorithmic_function> >::iterator it2 = fonctions.find(nom_fonction);

                ++index; //on passe après la parenthèse
                number_type x_value = somme(); //on évalue indépendamment l'expression qui suit la fonction (c'est à dire la valeur de x)

                if(it1 != fonctions_algo.end())
                {
                    try {
                        nombre = (*(*it1).second)(x_value);
                    }
                    //il faut convertir les messages d'erreurs pour bien spécifier que l'erreur se trouve dans la définition de la fonction,
                    //et non pas dans la chaîne analysée en ce moment
                    catch(const exception& e)
                    {
                        throw fonction_error_wrapper_exception(e, nom_fonction);
                    }
                }
                else if(it2 != fonctions.end())
                {

                    //on met à jour les définitions de variables et de fonctions pour que la suite tout ça etc.
                    fonctions[nom_fonction].variables = variables;
                    fonctions[nom_fonction].fonctions = fonctions;
                    fonctions[nom_fonction].set_var('x', x_value);

                    try {
                        nombre = fonctions[nom_fonction].eval();
                    }
                    //il faut convertir les messages d'erreurs pour bien spécifier que l'erreur se trouve dans la définition de la fonction,
                    //et non pas dans la chaîne analysée en ce moment
                    catch(const exception& e)
                    {
                        throw fonction_error_wrapper_exception(e, nom_fonction);
                    }
                }
                else
                    throw undefined_symbol_exception(nom_fonction);
            }
            else if(expression_evaluator<number_type, algorithmic_function>::is_alpha(expression[index]))
            {
                char variable = expression[index];
                if(!is_var_defined(variable))
                    throw undefined_symbol_exception(string(&variable, 1));

                number_type valeur = get_var(variable);
                nombre = valeur;
                ++index;
            }
            else
            {
                //sinon, on lit le nombre à la position courante
                for(; more() && is_digit(expression[index]); ++index) ;

                string nombre_str(expression, index_depart, index-index_depart);
                istringstream entree(nombre_str);
                entree >> nombre;
            }

            if(moins_unaire)
                return -nombre;
            else
                return nombre;
        }
    }

    template<typename number_type, typename algorithmic_function>
    bool expression_evaluator<number_type, algorithmic_function>::more()
    {
        return index < expression.length();
    }

    template<typename number_type, typename algorithmic_function>
    bool expression_evaluator<number_type, algorithmic_function>::implicit_mult()
    {
        /*Vérifie s'il n'y a pas de signe multiplier implicite
         *avec des variables (comme par exemple 2a(3+2) ou encore (x+2)(x-3)bc
         *vérifications : si un chiffre et une lettre se suivent (ou l'inverse)
         *                si deux lettres se suivent
         *                si un chiffre/une lettre suivent une parenthèse fermante (ou précèdent une parenthèse ouvrante)
         *                si une parenthèse fermante et une parenthèse ouvrante se suivent
         */
        return ( (expression_evaluator<number_type, algorithmic_function>::is_digit(expression[index-1]) && expression_evaluator<number_type, algorithmic_function>::is_alpha(expression[index])) ||
                 (expression_evaluator<number_type, algorithmic_function>::is_alpha(expression[index-1]) && expression_evaluator<number_type, algorithmic_function>::is_digit(expression[index])) ||
                 (expression_evaluator<number_type, algorithmic_function>::is_alpha(expression[index-1]) && expression_evaluator<number_type, algorithmic_function>::is_alpha(expression[index])) ||
                 (expression_evaluator<number_type, algorithmic_function>::is_alphanum(expression[index-1]) && expression[index] == '(') ||
                 (expression[index-1] == ')' && expression_evaluator<number_type, algorithmic_function>::is_alphanum(expression[index])) ||
                 (expression[index-1] == ')' && expression[index] == '(') );
    }

    template<typename number_type, typename algorithmic_function>
    bool expression_evaluator<number_type, algorithmic_function>::is_digit(char caractere)
    {
        return (caractere == '.' ||
                (caractere >= '0' && caractere <= '9'));
    }

    template<typename number_type, typename algorithmic_function>
    bool expression_evaluator<number_type, algorithmic_function>::is_alpha(char caractere)
    {
        return (caractere >= 'a' && caractere <= 'z') || (caractere >= 'A' && caractere <= 'Z') || caractere == '_';
    }

    template<typename number_type, typename algorithmic_function>
    bool expression_evaluator<number_type, algorithmic_function>::is_alphanum(char caractere)
    {
        return is_digit(caractere) || is_alpha(caractere);
    }

    template<typename number_type, typename algorithmic_function>
    bool expression_evaluator<number_type, algorithmic_function>::is_operator(char caractere)
    {
        return (caractere == '+' || caractere == '-' || caractere == '%'
                || caractere == '*' || caractere == '/' || caractere == '^');
    }

    template<typename number_type, typename algorithmic_function>
    bool expression_evaluator<number_type, algorithmic_function>::is_forbidden(char caractere)
    {
        return (!is_digit(caractere) && !is_operator(caractere) && !is_alpha(caractere)
                && caractere != '(' && caractere != ')' && caractere != ' ');
    }

    template<typename number_type, typename algorithmic_function>
    string expression_evaluator<number_type, algorithmic_function>::get_expression() const
    {
        return expression;
    }

    template<typename number_type, typename algorithmic_function>
    number_type expression_evaluator<number_type, algorithmic_function>::get_var(char variable) const
    {
        typename map<char, number_type>::const_iterator it = variables.find(variable);

        if(it == variables.end())
            return 0;
        else
            return (*it).second;
    }

    template<typename number_type, typename algorithmic_function>
    string expression_evaluator<number_type, algorithmic_function>::get_function(const string& nom) const
    {
        typename map<string, expression_evaluator<number_type, algorithmic_function> >::const_iterator it = fonctions.find(nom);

        if(it == fonctions.end())
            return "";
        else
            return (*it).second.get_expression();
    }

    template<typename number_type, typename algorithmic_function>
    algorithmic_function expression_evaluator<number_type, algorithmic_function>::get_algorithmic_function(const string& nom) const
    {
        typename map<string, /*expression_evaluator<number_type, algorithmic_function>::algorithmic_function*/ALGORITHMIC_FUNCTION>::const_iterator it = fonctions.find(nom);

        if(it == fonctions.end())
            return NULL;
        else
            return (*it).second;
    }

    template<typename number_type, typename algorithmic_function>
    void expression_evaluator<number_type, algorithmic_function>::set_expression(const string& expression)
    {
        this->expression = expression;
    }

    template<typename number_type, typename algorithmic_function>
    void expression_evaluator<number_type, algorithmic_function>::set_var(char variable, number_type valeur)
    {
        variables[variable] = valeur;
    }

    template<typename number_type, typename algorithmic_function>
    void expression_evaluator<number_type, algorithmic_function>::set_function(const string& nom, const string& expression)
    {
        //on créer une nouvelle fonction seulement si elle n'est pas déjà définie parmis les fonctions algorithmiques ou prédéfinies
        //pour éviter les conflits
        if(fonctions_algo.find(nom) == fonctions_algo.end())
            fonctions[nom] = expression;
    }

    template<typename number_type, typename algorithmic_function>
    void expression_evaluator<number_type, algorithmic_function>::set_algorithmic_function(const string& nom, algorithmic_function fonction)
    {
        //si la fonction existe déjà parmis les fonctions définies par l'utilisateur, on la supprime de la liste avant de l'insérer
        //parmis les fonctions algorithmiques
        typename map<string, expression_evaluator<number_type, algorithmic_function> >::iterator it = fonctions.find(nom);
        if(it != fonctions.end())
            fonctions.erase(it);
        fonctions_algo[nom] = fonction;
    }

    template<typename number_type, typename algorithmic_function>
    bool expression_evaluator<number_type, algorithmic_function>::is_var_defined(char variable) const
    {
        return variables.find(variable) != variables.end();
    }

    template<typename number_type, typename algorithmic_function>
    bool expression_evaluator<number_type, algorithmic_function>::is_function_defined(const string& nom) const
    {
        return fonctions.find(nom) != fonctions.end() || fonctions_algo.find(nom) != fonctions_algo.end();
    }

    template<typename number_type, typename algorithmic_function>
    number_type expression_evaluator<number_type, algorithmic_function>::eval()
    {
        if(expression.find(' ') != string::npos)
            boost::algorithm::erase_all(expression, " "); //suppression des espaces

        check_syntax();
        index = 0;
        return somme();
    }

    template<typename number_type, typename algorithmic_function>
    void expression_evaluator<number_type, algorithmic_function>::check_syntax() const throw(syntax_exception)
    {
        if((expression_evaluator<number_type, algorithmic_function>::is_operator(expression[0]) && expression[0] != '-')
           || expression_evaluator<number_type, algorithmic_function>::is_operator(expression[expression.length()-1]))
            throw syntax_exception("l'expression commence ou se termine par un opérateur");

        register int i;
        register int compteur = 0;

        for(i = 0; i < expression.length(); ++i)
        {
            if(expression_evaluator<number_type, algorithmic_function>::is_forbidden(expression[i]))
                throw syntax_exception("caractère interdit", expression, i);
            if(expression[i] == '(')
                ++compteur;
            if(expression[i] == ')')
                --compteur;
        }

        if(compteur != 0)
            throw syntax_exception("le nombre de parenthèses ouvrantes ne correspond pas au nombres de parenthèses fermantes.");
    }

    template<typename number_type, typename algorithmic_function>
    expression_evaluator<number_type, algorithmic_function>& expression_evaluator<number_type, algorithmic_function>::operator=(const expression_evaluator<number_type, algorithmic_function>& source)
    {
        expression = source.expression;
        variables = source.variables;
        fonctions = source.fonctions;
        fonctions_algo = source.fonctions_algo;
    }

    template<typename number_type, typename algorithmic_function>
    expression_evaluator<number_type, algorithmic_function>::operator number_type()
    {
        return eval();
    }

    template<typename number_type, typename algorithmic_function>
    expression_evaluator<number_type, algorithmic_function>::~expression_evaluator()
    {
    }
}

#endif /* ndef _CALCUL_HPP_ */

Conclusion

MAJ effectuée pour les variables ainsi que pour les fonctions personnalisables/algorithmiques et usuelles.
 

Fichier Zip

Pour les "Membres Club", vous pouvez télécharger directement un fichier contenu dans le zip sans télécharger le zip en entier !

Télécharger le zip

Historique

17 septembre 2007 18:52:15 :
Support des opérateurs ^ et %. Support du moins unaire. Allègement de la fonction de vérification de syntaxe (les doublons [ou plus] d'opérateurs sont détectés pendant l'analyse et non pas avant par la fonction check_syntax). Le modulo transforme les deux opérandes en entier pour pouvoir effectuer l'opération. ex: 5.26%3 = 5%3 = 2 ; 2/4%x = 0%x = 0.
18 septembre 2007 20:30:29 :
mise à jour de la source (celle de la source hors du zip n'avait pas fonctionné)
06 octobre 2007 15:21:34 :
Ajout de la gestion des variables. Utiliser les méthodes set_var()/get_var()/is_var_defined(). Support des multiplications implicites : par exemple, on peut écrire 2*a*b*(3+z)(3-z) comme ceci : 2ab(3+z)(3-z) Correction d'un bug avec le moins unaire : 12-2 donnait comme résultat 14, car il comprenait 12--2 à cause d'un test sur le moins unaire non pertinent (remplacé maintenant par un booléen) dans la fonction terme().
06 octobre 2007 15:24:17 :
Ajout de la gestion des variables. Utiliser les méthodes set_var()/get_var()/is_var_defined(). Support des multiplications implicites : par exemple, on peut écrire 2*a*b*(3+z)(3-z) comme ceci : 2ab(3+z)(3-z) Correction d'un bug avec le moins unaire : 12-2 donnait comme résultat 14, car il comprenait 12--2 à cause d'un test sur le moins unaire non pertinent (remplacé maintenant par un booléen) dans la fonction terme().
06 octobre 2007 21:05:56 :
Ajout du support des fonctions personnalisables (mais pas des fonctions de référence) ; Voir méthodes set_fonction()/get_fonction()/is_function_defined(). Les fonctions doivent être précédées du suffixe _ dans les expressions pour être différenciées des variables : Ex: _abc(5) : la fonction abc, avec x = 5 abc(5) : a*b*c*5 mais pas dans les méthodes set/get. Corrections de bugs.
12 octobre 2007 19:32:44 :
Ajout des fonctions usuelles (abs, acos, asin, atan, floor, ceil, exp, log, log10, cos, cosh, sin, sinh, tan, tanh) et possibilité de définir des fonctions "algorithmiques" : des fonctions qui au lieu d'être une simple expression du genre "2x + 3", se reportent sur une fonction dans le programme (d'ailleurs les fonctions usuelles sont en fait des fonctions algorithmiques qui renvoient aux fonctions mathématiques fournies par le C : cos, acos, etc...)

Commentaires et avis

signaler à un administrateur
Commentaire de Forman le 16/09/2007 12:33:52

Bonjour,

code intéressant pour le principe mais je ne crois pas qu'il soit judicieux de parler de "puissance de la récursivité" dans ce cas. En effet, les analyseurs "standards" utilisent des automates finis à pile d'états pour fonctionner, et tout est complètement itératif, c'est à dire que si tu donnes une expression compliquée avec un "arbre d'évaluation" très profond, ce sont juste des boucles qui tournent. Seule la pile d'états peut augmenter de taille, au prix de quelques realloc.

En revanche avec la récursivité il est très facile de remplir la stack avec les appels de fonctions récursives imbriquées. En plus, il est vraisemblable que ça ira plus lentement (la version itérative est très facilement optimisable). Je n'ai pas compilé ton code, mais as-tu essayé de lui donner une expression très, très longue? Vraisemblablement ça provoquera un stack overflow.

Mais bon, c'est vrai que ça rend le code plus lisible...

signaler à un administrateur
Commentaire de lovejava le 16/09/2007 14:06:36

Yop,

en effet la récursivité coûte ici assez cher...c'est plutôt la lisibilité du code et l'élégance que je trouve sympa surtout ^^ on est d'accord qu'un système d'exploitation de calculatrice n'utiliserait pas ce code...

signaler à un administrateur
Commentaire de luhtor le 03/10/2007 18:54:12

Je pense aussi qu'il faut également privilégier la lisibilité et l'élégance du code. De toute facon, un analyseur de ce type n'a pas besoin d'etre performant vu la puissance des ordinateurs. Qu'il mette 1ms ou 10ms, ca sera la meme chose pour l'utilisateur.

Avant de faire un stackoverflow, a mon avis, tu pourras en mettre des parenthèses. Pour remplir 1mo d'appel de fonction, faut y aller. Dans tous les cas, suffit juste de faire une petite vérification pour éviter le stackoverflow.

signaler à un administrateur
Commentaire de luhtor le 03/10/2007 18:57:50

Il est dommage qu'on ne puisse pas ajouter de variable dans une expression dont on demanderait ensuite l'évaluation en fournissant les valeurs de chaque variable. Sans cela, cet analyseur me semble inutile (en tout cas pour moi).

signaler à un administrateur
Commentaire de lovejava le 06/10/2007 15:30:18

Luthor, voila, j'ai rajouté le support des variables, merci pour l'idée (je n'aurais pas eu tendance à forcément l'implémenter sinon)
Je prévois normalement pour la prochaine version le support des fonctions usuelles mais également de ses propres fonctions définissables par exemple par : f:x ---> 3x+2abc(1-2^3)

+

signaler à un administrateur
Commentaire de lovejava le 06/10/2007 21:08:19

Ajout des la gestion des fonctions.

Ajouter un commentaire

Discussions en rapport avec ce code source dans le forum

math calcul formel [ par yvessimon ] yvessimon Une classe d'algèbre (calcul matriciel) performante ? [ par Sylphe67 ] Bonjour à tous,En une phrase, je recherche une classe template permettant d'effectuer du calcul matriciel performant sur des matrices carrées de taill Conversion d'entier en base 2^32 vers base 10 [ par le_duche ] Bonjour !Je chercher je cherche, mais je ne trouve pas d'idée.Dans le cadre de l'implémentation d'une classe de grands entiers, je souhaite pouvoir co Calcul période sinus sur Pic [ par spiderloic74 ] <meta name="CREATED" content="20080904;83 Petit problème API Windows [ par TCHATTE ] Bonjour,je suis en train d'essayer d'apprendre comment fonctionne l'api windows ... j'ai a peu près compris comment afficher tout ce que je veux par c Expression régulière POSIX abondante [ par sebclick ] Bonjour,Je cherche à traiter un fichier XML par un programme C à l'aide d'expression régulière pour récupérer le texte contenu entre deux balises.Voic Calcul d'une puissance en C avec pow() [ par Smallstones2 ] Bonjour, pourriez vous m'aider. Voici mon programme mais celui ci ne marche pas: #include #include main () { float x, y,z; printf ("Entrez x : " Aidez-moi!! Calcul de primitive - Methode des trapèzes [ par M1n1d0u ] Bonjour a tous,J'ai besoin de votre aide pour programmer le calcul d'une primitive par la methode des trapèzes.Je ne sais pas où chercher, et  je ne s probleùme de math [ par mariii ] aidez moi a resoudre mon probleme!!!merci un constructeur propose une maison d'un volume de 900 CM CUBE.Pour une exposition,on realise une maquette


Nos sponsors

Sondage...

CalendriCode

Juillet 2009
LMMJVSD
  12345
6789101112
13141516171819
20212223242526
2728293031  

Consulter la suite du CalendriCode

Téléchargements

Comparez les prix Nouvelle version

Photothèque Nouveau !



Développement réalisé par Nicolas SOREL (Nix) avec l'aide de : Cyril DURAND et Emmanuel (EBArtSoft), Merci à Vincent pour ses précieux conseils
CodeS-SourceS.com© Toute reproduction même partielle est interdite sauf accord écrit du Webmaster
CodeS-SourceS.com© est une marque déposée tous droits réservés
Temps d'éxécution de la page : 0,468 sec

Google Coop CodeS-SourceS Google Coop CodeS-SourceS


Certaines images présentes sur le site (notament certains avatars) sont issues des collections IconShock, donc si vous souhaitez utiliser ces icons vous devez les acheter, ne les copiez pas et ne utilisez pas dans vos sites et applications sans les avoir commandé.