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 :

Optimisation / parser math�matique


Sujet :

C++

  1. #1
    Membre �m�rite
    Inscrit en
    Janvier 2005
    Messages
    711
    D�tails du profil
    Informations forums :
    Inscription : Janvier 2005
    Messages : 711
    Par d�faut Optimisation / parser math�matique
    Bien le bonjour,

    je developpe un logiciel auquel j'aurais besoin d'ajouter un parser mathematique. j'ai commenc� a bidouiller un peu flex/bison (c'est assez impressionant d'ailleurs !! ) et je devrais arriver a m'en sortir. je me pose une question neanmoins :

    je vais donc parser mn expression, et construire a partir d'elle un arbre qui me permettra ensuite d'evaluer la formule e donnant des valeurs aux variables. pour cela je vais donc creer une classe mere qui sera le type de base de l'arbre, et des classes filles pour chaque "genre" : variable, fonction, operateur binaire, operateur unaire, etc... et c'est la que je m'interroge : vaut il mieux creer UNE classe pour chacun de ses genres, et gerer a l'interieur avec des switch ou des ifs, genre :

    Code : S�lectionner tout - Visualiser dans une fen�tre � part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    class nodeOperator : derive de node
    .....
     
    fonction eval()
    switch operateur
    case "+" renvoie filsGauche->eval() + filsDroit->eval()
    case "*" renvoie filsGauche->eval() * filsDroit->eval()
    etc..
    ou creer une classe fille pour CHAQUE operateur ou fonction ce qui est un peu plus long mais evite d'avoir des series de tests conditionnels ?

    merci..

    accessoirement, y a t il des choses auquelles il faut faire gaffe et auquelle je risque de ne pas penser ?? si ca peut m'eviter des erreurs au depart :-)

  2. #2
    Expert confirm�
    Avatar de Mat.M
    Profil pro
    D�veloppeur informatique
    Inscrit en
    Novembre 2006
    Messages
    8 538
    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 538
    Par d�faut
    et c'est la que je m'interroge : vaut il mieux creer UNE classe pour chacun de ses genres, et gerer a l'interieur avec des switch ou des ifs, genre :
    Ben je crois que le mieux c'est de cr�er une classe param�trable c.a.d avec template...
    ( sur codeproject.com je crois qu'il y a un exemple comme cela )
    sinon tu peux prendre les conteneurs de la STL et empiler les genres...
    Non ? Meilleure id�e ?

  3. #3
    Membre �m�rite
    Inscrit en
    Janvier 2005
    Messages
    711
    D�tails du profil
    Informations forums :
    Inscription : Janvier 2005
    Messages : 711
    Par d�faut
    je ne suis pas sur de te suivre. quand je parlais de genre, je ne voulais pas parler de type au sens entier, double.. mais bien d'element dont le role est different (operateur, variable, valeur numerique..), et dont le traitement vont etre different ! je ne vois pas trop comment je pourrais me depatouiller avec des templates... si tu povais m'en dire un peu plus ! merci :-)

  4. #4
    Membre �m�rite Avatar de HanLee
    Profil pro
    Inscrit en
    Mai 2004
    Messages
    738
    D�tails du profil
    Informations personnelles :
    �ge : 38
    Localisation : France, Rh�ne (Rh�ne Alpes)

    Informations forums :
    Inscription : Mai 2004
    Messages : 738
    Par d�faut
    Tu as regard� du c�t� de boost::spirit ?

    Et si tu voulais tout coder toi-m�me, comme en g�n�ral une expression est un type r�cursif, et en m�me temps une somme disjointe de plusieurs types, soit tu peux essayer boost::variant, qui permet d'en faire.

    La m�thode polymorphisme avec h�ritage, j'aime pas trop, il faut que tu utilises des visiteurs pour pouvoir ajouter simplement des fonctions, sinon tu serais oblig� de modifier le code de chaque classe, ce qui est assez lourd.

    Ca revient au m�me que boost::variant, mais plus long.

  5. #5
    Membre �m�rite
    Inscrit en
    Janvier 2005
    Messages
    711
    D�tails du profil
    Informations forums :
    Inscription : Janvier 2005
    Messages : 711
    Par d�faut
    si je ne m'abuse, boost::spirit est un analyseur lexical/syntaxique, et comme je le disais je n'ai (pour l'instant ) pas trop de probleme de ce cot� la, en utilisant bison et flex..

    pour ce qui est du polymorphisme, je suis bien oblig� dans la mesure ou chaque "genre" de noeud va necessiter un traitement different, donc un boost::variant ne suffira pas.. enfin je crois

    il faut bien comprendre que l'expression math�matique n'est pas evalu�e au moment du parsing (c'est comme ca qu'on dit ?) mais doit etre stock�e (a priori sous forme d'arbre)...

  6. #6
    Inactif  
    Profil pro
    Inscrit en
    Mars 2004
    Messages
    743
    D�tails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2004
    Messages : 743
    Par d�faut
    Citation Envoy� par jobherzt
    je ne suis pas sur de te suivre. quand je parlais de genre, je ne voulais pas parler de type au sens entier, double.. mais bien d'element dont le role est different (operateur, variable, valeur numerique..), et dont le traitement vont etre different ! je ne vois pas trop comment je pourrais me depatouiller avec des templates... si tu povais m'en dire un peu plus ! merci :-)
    Je crois qu'� ta place j'utiliserais des fonctions virtuelles. Ca t'�viterais de faire des switchs, qu'il te faudrait remettre � jour � chaque nouvelle classe.
    Ex:

    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
     
    class expr
    {
      ...
      virtual expr *eval() =0;
      virtual expr *plus() =0;
    };
     
    typedef auto_pointer<expr> Expr;
     
    template<class T>
    class value : public expr
    {
      T val;
      ...
      virtual expr *operator() { return new value(val); }
      virtual expr *plus(expr *b) { return new value(val+b->val); }
    };
     
    class binop: public expr
    {
      Expr a,b;
      ...
    };
     
    class plusop : public binop
    {
      virtual expr *eval() { return a->eval()->plus(b->eval()); }
    };

  7. #7
    Membre �m�rite
    Inscrit en
    Janvier 2005
    Messages
    711
    D�tails du profil
    Informations forums :
    Inscription : Janvier 2005
    Messages : 711
    Par d�faut
    Citation Envoy� par Charlemagne
    Ca t'�viterais de faire des switchs, qu'il te faudrait remettre � jour � chaque nouvelle classe.
    pourquoi est ce que je devrais remettre a jour mes switch a chaque nouvelle classe ? l'ajout d'une classe n'a pas de raison d'avoir d'effet sur les autres.... je te montre le debut de ce que j'ai fait (c'est encore une sorte de "brouillon" avant d'attaquer vraiment). je n'ai pas besoin de template a priori, mes constantes numeriques seront toujours du meme type "REEL". ma question serait plutot de savoir si dans le cas ou j'aurais vraiment beaucoup (mais genre vraiment :-) ) a evaluer mon expression avec different parametetre, le fait d'avoir a effectuer un switch a chaque fois que j'ai une operation "coute" plus en temps de calcul que d'avoir une classe "d�di�" pour chaque operateur ou fonction ! (je ne suis pas clair, hein )

    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
     
    #include "pave.h"
     
    class node
    {
    	protected:
    		interval value;
     
    	public:
     
    		node()
    		{
    		}
    		virtual interval eval(const pave& parameters,const REEL& variable) {}
    		//virtual void contract(pave& parameters, const REEL& variable ) const =0;
    };
     
    class nodeBinaryOp : public node
    {
    	protected:
    		char op;
    		node * nR;
    		node * nL;
    	public:
    		nodeBinaryOp(char _op, node * _nL, node * _nR)
    		{
    			op=_op;
    			nL=_nL;
    			nR=_nR;
    		}
     
    		interval eval(const pave& parameters,const REEL& variable)
    		{
    			switch(op)
    			{
    				case '+' : value = nL->eval(parameters, variable) + nR->eval(parameters, variable); break;
    				case '*' : value = nL->eval(parameters, variable) * nR->eval(parameters, variable);break;
    				case '-' : value = nL->eval(parameters, variable) - nR->eval(parameters, variable);break;
    				case '/' : value = nL->eval(parameters, variable) / nR->eval(parameters, variable);break;
    			}
     
    			return value;
    		}
    };
     
    class nodeConstant : public node
    {
    	public:
    	nodeConstant(interval _val)
    	{
    		value=_val;
    	}
    	interval eval(const pave& parameters,const REEL& variable)
    	{
    		return value;
    	}
    };

  8. #8
    Inactif  
    Profil pro
    Inscrit en
    Mars 2004
    Messages
    743
    D�tails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2004
    Messages : 743
    Par d�faut
    A priori tu veux faire une "calculatrice".
    Si c'est le cas, �a simplifie la programmation par rapport � concevoir un "Math�matica".
    Et tu n'auras probablement qu'un seul switch pour ta fonction d'�valuation. Mais si tu comptes rajouter plus de fonctionnalit�s, t'auras toujours plus de switchs, qu'il te faudra remettre � jour � chaque nouvelle classe (nombres complexes, variables, fonctions scientifiques, ...).

    Je conseillerais plut�t de faire des classes d�di�es, plut�t que de regrouper les 4 op�rations binaires.
    Mais regroupe les dans une classe de base commune, pour factoriser le code.

    Et puis, je m'arrangerais pour que la fonction d'�valuation ne re�oive aucun param�tre (je vois pas bien � quoi correspond ton "pave").
    Mais une classe "variable" pourrait tr�s bien retrouver la valeur associ�e � partir d'un nom.
    par exemple du genre:
    Code : S�lectionner tout - Visualiser dans une fen�tre � part
    1
    2
    3
    4
    5
    6
    7
    8
    class variable : public expr
    {
      static map<string,expr*> vars;
     
      map<string,expr*>::iterator it;
      variable (const string &s) : expr() { it=vars.insert(s).first; }
      virtual expr *eval() { return *it; }
    };

  9. #9
    Membre �m�rite Avatar de HanLee
    Profil pro
    Inscrit en
    Mai 2004
    Messages
    738
    D�tails du profil
    Informations personnelles :
    �ge : 38
    Localisation : France, Rh�ne (Rh�ne Alpes)

    Informations forums :
    Inscription : Mai 2004
    Messages : 738
    Par d�faut
    boost::variant peut aussi tr�s bien convenir.

    Regarde comment c'est fait ici :

    http://www.boost.org/doc/html/varian...ursive-wrapper

    Tu n'es pas oblig� d'utiliser la m�me conception pr�sent�e. Moi personnellement � la place de mettre binary_op sous forme de classe template, je rajouterais un autre param�tre � son constructeur de type boost::function<float (float, float)> par exemple qui me permettrait de construire des nouvelles fonctions dynamiquement, de mani�re simple, au lieu de rajouter � chaque fois des nouveaux types dans la d�finition.

  10. #10
    Membre �m�rite
    Inscrit en
    Janvier 2005
    Messages
    711
    D�tails du profil
    Informations forums :
    Inscription : Janvier 2005
    Messages : 711
    Par d�faut
    Bon, c'est vrai que je n'ai pas donn� beaucoup de d�tail

    En fait ce que je fais ne ressemble vraiment pas a une calculatrice.. meme si ca n'est pas un mathrmatica, c'est quand meme un chouille compliqu�. La difference principale est que dans une calculatrice une expression (contenant eventellement des variables qui ont �t� pr�definies dans ce cas) est �valu� au moment ou elle est pars�. Dans cette situation, on verifie la puissance de lex/yacc, puisque ca se fait (y compris avec une gestion simplifi�e de variable) en quelques minutes (ltt�ralement !!) et sans avoir a construire d'arbre.

    en 2 mots, il ne s'agit pas d'un systeme qui me permettrait de faire :

    d'ailleurs mon programme ne sera pas interactif...

    Dans mon cas au contraire, lorsque je parse mon expression, je ne dois pas l'�valuer mais la stocker (donc dans un arbre) pour pouvoir la manipuler et l'utiliser ensuite. c'est pour ca que j'ai besoin d'une structure "explicite" d'abre, c'est aussi pour ca que ma fonction d'evaluation prend des parametres. c'est pour ca enfin que je n'aurais pas besoin d'un systeme complexe de gestion de variables, je n'aurais qu'a fournir a ma fonction d'evaluation une liste de valeur. pour info, il s'agit d'un logiciel de modelisation/optimisation.

    par ailleurs, ne confondons pas les "types" (entier, float, complexe..) et ce que j'ai appell� les "genres" a defaut d'un mot plus appropri� (valeur numerique, operateur (+,-..) fonction (sin, exp..)).

    c'est pour ca que je n'ai pas tellement d'autre solution que d'utiliser le polymorphisme, vu que je fais coexister des "genres" diff�rents dans un meme arbre, et cela ne se regle pas a coup de template ou de boost::variant vu que chaque genre necessite une structure de classe bien specifique... (les operateurs binaires ont 2 "fils" alors que sin et cos n'en ont qu'un, par exemple..)

    Si j'avais besoin d'utiliser plusieurs types (mais ca n'est pas le cas), je pourrais sans doute g�r�r ca avec des templates. mais je n'aurais pas a modifier mes "switch".

    si je veux rajouter un genre ou un element d'un genre, au pire ca me fera creer une nouvelle classe heritant de "node" ou rajouter une ligne dans un switch, mais rien de plus..

    ceci �tant pos�, ma question ne concernait donc que la vitesse relative de 2 systemes, a savoir :

    - exploiter a fond le polymorphisme et utiliser une classe pour plus, une classe pour moins, etc.
    - n'utiliser le polymorphisme que pour distinguer les differents genre, puis "switcher" a l'interieur de chaque classe. (une classe pour les operateurs binaires, puis un switch pour savoir s'il s'agit d'un plus ou d'un moins..) plus simple a ecrire, mais peut etre plus lent ??

    voila, j'espere que c'est plus clair :-) et sinon, ne vous cassez pas la tete, je verrais bien a l'usage !!

  11. #11
    Membre �m�rite Avatar de HanLee
    Profil pro
    Inscrit en
    Mai 2004
    Messages
    738
    D�tails du profil
    Informations personnelles :
    �ge : 38
    Localisation : France, Rh�ne (Rh�ne Alpes)

    Informations forums :
    Inscription : Mai 2004
    Messages : 738
    Par d�faut
    Selon ta solution, bah, moi je penserais que plus de types possibles serait plus rapide, mais j'en sais trop rien. Fais des tests !
    Faire un switch revient � faire du polymorphisme � la main, qui est sera peut-�tre moins efficace que celui impl�ment� avec la table des fonctions virtuelles.

    <Digression>
    Et sinon, les genres dont tu parles, ce sont bien des types, mais r�cursifs. On ne les �value pas, ils forment des noeuds de l'expression sous forme arborescente.
    Ce sont des sous-types d'une expression, qui peut se repr�senter par cette d�finition :

    Code : S�lectionner tout - Visualiser dans une fen�tre � part
    1
    2
    3
    4
    EXPRESSION =
       Réel
    |  Expression_unaire : (Réel -> Réel) * EXPRESSION 
    |  Expression_binaire : (Réel * Réel -> Réel) * EXPRESSION * EXPRESSION
    O� le caract�re '*' d�signe le produit cart�sien. Avec �a tu peux construire la majorit� des expressions analytiques usuelles.

    Donc je pense que ton but est de faire une arborescence qui � l'expression sin(3 + cos(4)) associe :

    Expression_unaire(sin, Expression_binaire(+, 3, Expression_unaire(cos, 4)))

    </Digression>

    ------

    Et sinon, je ne sais pas pourquoi tu dis que la m�thode avec boost::variant ne permet pas de faire ce que tu fais pour l'instant. Je sais pas si tu as lu ce que je t'ai montr� :/.

    En tout cas je te passe en pi�ce jointe un code en exemple qui compile que j'viens de faire, dis moi si c'est �quivalent � ce que tu as d�j� fait.
    Fichiers attach�s Fichiers attach�s

  12. #12
    Membre �m�rite
    Inscrit en
    Janvier 2005
    Messages
    711
    D�tails du profil
    Informations forums :
    Inscription : Janvier 2005
    Messages : 711
    Par d�faut
    oui, ok ! ce que je voulais c'etais smplement distinguer les 2.. c'est por ca que e les ai appel�s genre :-)

    sinon, pour boost variant, j'avais essay� de regarder un peu.. je connais mal, donc je me trompe peut etre, mais je ne suis ps sur que ca corresponde a ce que je veux. en effet, je ne vais pas avoir seulement a l'evaluer, mais aussi a faire des manipulations plus complexe, a parcourir l'arbre dans tous les sens... donc chacun de mes "types/genre/classe fille" va avoir des metodes assez particuliere... mais j'avoue que c'est assez impressionnant, je me demande si je ne pourrais pas essayer un mixage entre ca et ce que j'ai :-) a essayer..

  13. #13
    Membre �m�rite
    Inscrit en
    Janvier 2005
    Messages
    711
    D�tails du profil
    Informations forums :
    Inscription : Janvier 2005
    Messages : 711
    Par d�faut
    meme si finalement je n'utiliserais pas boost::variant, ton exemple m'a mis sur la bonne piste :-) utiliser un pointeur de fonction !! petit exemple :

    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
     
    class nodeUnaryFunc : public node
    {
    	protected:
    		node * nB;
    		interval (*ptrEvalFunc)(interval);
    	public:
    		nodeUnaryFunc(node * _nB, string funcName)
    		{
    			nB=_nB;
    			if(funcName == "exp")
    			{
    				ptrEvalFunc=&iExp;
    			}
                            //d'autre fonctions à venir....
    		}
     
    		interval eval(const pave& parameters,const REEL& variable)
    		{
    			return (*ptrEvalFunc)(nB->eval(parameters, variable));
    		}
    };
    ainsi, j'aurais toujours une serie de test, mais seulement dans le constructeur, donc ca sera fait une fois pour toute.. je pense que ca tient la route, qu'en pensez vous ??

  14. #14
    Membre �m�rite Avatar de HanLee
    Profil pro
    Inscrit en
    Mai 2004
    Messages
    738
    D�tails du profil
    Informations personnelles :
    �ge : 38
    Localisation : France, Rh�ne (Rh�ne Alpes)

    Informations forums :
    Inscription : Mai 2004
    Messages : 738
    Par d�faut
    J'aime pas l'id�e de faire �a dans le constructeur.

    Ton noeud nodeUnaryFunc n'est pas un objet dans le sens POO classique, o� ya un utilisateur et tout �a. C'est cens� repr�senter un objet math�matique tout ce qu'il y a de plus simple : une fonction unaire, et une expression en tant qu'argument.
    Moi je pr�f�re un constructeur qui contient un ptr de fonction et un pointeur vers une expression, et � l'appel du constructeur, tu utiliserais une fonction qui convertit les strings en pointeurs de fonction.

    Sinon, tu es oblig� de modifier le code de ta classe � chaque fois, de conna�tre toutes les fonctions qui font intervenir avant de d�finir ton constructeur.

    Je trouve que �a rend le code mal d�coup� et moins synth�tique.

    -----

    sinon, pour boost variant, j'avais essay� de regarder un peu.. je connais mal, donc je me trompe peut etre, mais je ne suis ps sur que ca corresponde a ce que je veux. en effet, je ne vais pas avoir seulement a l'evaluer, mais aussi a faire des manipulations plus complexe, a parcourir l'arbre dans tous les sens... donc chacun de mes "types/genre/classe fille" va avoir des metodes assez particuliere...
    Mais le type boost::variant que j'ai d�fini est la d�finition d'une expression sous forme arborescente selon la d�finition que je t'ai montr�e !

    Tu le manipules exactement comme un arbre.
    Tu traites chacun des sous-types s�par�ment (c'est pour �a qu'on d�finit plusieurs fonctions dans le visiteur).

    Tu peux enlever la fonction unaire englobant une expression si elle existe par exemple, tr�s facilement.

Discussions similaires

  1. Parser une formule math�matique
    Par rambc dans le forum G�n�ral JavaScript
    R�ponses: 1
    Dernier message: 23/11/2010, 19h53
  2. Composant Parser math�matique
    Par rvzip64 dans le forum Composants VCL
    R�ponses: 5
    Dernier message: 07/07/2010, 23h23
  3. Biblioth�que d'optimisation math�matique
    Par shams dans le forum Biblioth�ques
    R�ponses: 2
    Dernier message: 27/11/2009, 10h50
  4. Parser Math�matique C# ?
    Par tamiii dans le forum G�n�ral Dotnet
    R�ponses: 1
    Dernier message: 23/09/2008, 16h39
  5. Parser Java pour fonction math�matiques.
    Par abdelhamidem dans le forum API standards et tierces
    R�ponses: 2
    Dernier message: 20/05/2008, 00h19

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