web-dev-qa-db-fra.com

analyser une expression mathématique en c ++

J'ai une question sur l'analyse des arbres:

J'ai une chaîne (expression mathématique estring), par exemple: (a+b)*c-(d-e)*f/g. Je dois analyser cette expression dans un arbre:

class Exp{};
class Term: public Exp{
    int n_;
}

class Node: Public Exp{
    Exp* loperator_;
    Exp* roperator_;
    char operation; // +, -, *, /
}

Quel algorithme puis-je utiliser pour construire un arbre qui représente la chaîne d’expression ci-dessus?

23
Hal Nuevemil

Utilisez l'algorithme Shunting-yard . La description de Wikipédia est assez complète, j'espère que cela suffira.

Vous pouvez également essayer d'écrire une grammaire formelle, par exemple une grammaire parsing-expression , et utiliser un outil pour générer un analyseur. Ce site sur les PEG répertorie 3 bibliothèques C/C++ pour l'analyse PEG.

10
Kos

(a+b)*c-(d-e)*f/g est une expression in-fix. 

Pour créer facilement un tree , convertissez-le d'abord en une expression de préfixe.

Dans l'exemple, le préfixe .__ de (A * B) + (C / D) est + (* A B) (/ C D)

     (+)            
     / \        
    /   \       
  (*)    (/)         
  / \   /  \        
 A   B C    D   

 ((A*B)+(C/D))  

Votre arbre ressemble alors à a + en tant que nœud racine. Vous pouvez continuer à renseigner les sous-arbres gauche et droit, concernant chaque opérateur.

En outre, this link explique en détail l'analyse syntaxique de descente récursive et peut être implémenté.

5
Anirudh Ramanathan

La première étape consiste à écrire une grammaire pour vos expressions. La deuxième étape d'un cas aussi simple consiste à écrire un analyseur récursif de descente, c'est l'algorithme que je recommanderais. Voici la page wiki sur les analyseurs de descente récursive qui a une belle implémentation en C.

http://en.wikipedia.org/wiki/Recursive_descent_parser

4
jahhaj
#include <algorithm>
#include <iostream>
#include <string>
#include <cctype>
#include <iterator>

using namespace std;

class Exp{
public:
//  Exp(){}
    virtual void print(){}
    virtual void release(){}
};
class Term: public Exp {
    string val;
public:
    Term(string v):val(v){}
    void print(){
        cout << ' ' << val << ' ';
    }
    void release(){}
};

class Node: public Exp{
    Exp *l_exp;
    Exp *r_exp;
    char op; // +, -, *, /
public:
    Node(char op, Exp* left, Exp* right):op(op),l_exp(left), r_exp(right){}
    ~Node(){
    }
    void print(){
        cout << '(' << op << ' ';
        l_exp->print();
        r_exp->print();
        cout  << ')';
    }
    void release(){
        l_exp->release();
        r_exp->release();
        delete l_exp;
        delete r_exp;
    }
};

Exp* strToExp(string &str){
    int level = 0;//inside parentheses check
    //case + or -
    //most right '+' or '-' (but not inside '()') search and split
    for(int i=str.size()-1;i>=0;--i){
        char c = str[i];
        if(c == ')'){
            ++level;
            continue;
        }
        if(c == '('){
            --level;
            continue;
        }
        if(level>0) continue;
        if((c == '+' || c == '-') && i!=0 ){//if i==0 then s[0] is sign
            string left(str.substr(0,i));
            string right(str.substr(i+1));
            return new Node(c, strToExp(left), strToExp(right));
        }
    }
    //case * or /
    //most right '*' or '/' (but not inside '()') search and split
    for(int i=str.size()-1;i>=0;--i){
        char c = str[i];
        if(c == ')'){
            ++level;
            continue;
        }
        if(c == '('){
            --level;
            continue;
        }
        if(level>0) continue;
        if(c == '*' || c == '/'){
            string left(str.substr(0,i));
            string right(str.substr(i+1));
            return new Node(c, strToExp(left), strToExp(right));
        }
    }
    if(str[0]=='('){
    //case ()
    //pull out inside and to strToExp
        for(int i=0;i<str.size();++i){
            if(str[i]=='('){
                ++level;
                continue;
            }
            if(str[i]==')'){
                --level;
                if(level==0){
                    string exp(str.substr(1, i-1));
                    return strToExp(exp);
                }
                continue;
            }
        }
    } else
    //case value
        return new Term(str);
cerr << "Error:never execute point" << endl;
    return NULL;//never
}

int main(){
    string exp(" ( a + b ) * c - ( d - e ) * f / g");
    //remove space character
    exp.erase(remove_if(exp.begin(), exp.end(), ::isspace), exp.end());
    Exp *tree = strToExp(exp);
    tree->print();
    tree->release();
    delete tree;
}
//output:(- (* (+  a  b ) c )(/ (* (-  d  e ) f ) g ))
3
BLUEPIXY

Vous pouvez utiliser cette grammaire pour créer votre expression.

exp:
    /* empty */
  | non_empty_exp { print_exp(); }
  ;
non_empty_exp:
    mult_div_exp
  | add_sub_exp
  ;
mult_div_exp:
    primary_exp
  | mult_div_exp '*' primary_exp { Push_node('*'); }
  | mult_div_exp '/' primary_exp { Push_node('/'); }
  ;
add_sub_exp:
    non_empty_exp '+' mult_div_exp { Push_node('+'); }
  | non_empty_exp '-' mult_div_exp { Push_node('-'); }
  ;
primary_exp:
  | '(' non_empty_exp ')'
  | NUMBER { Push_term($1); }
  ;

Et ce qui suit pour votre lexer.

[ \t]+   {}
[0-9]+   { yylval.number = atoi(yytext); return NUMBER; }
[()]     { return *yytext; }
[*/+-]   { return *yytext; }

L'expression est construite au fur et à mesure, en utilisant les routines suivantes:

std::list<Exp *> exps;

/* Push a term onto expression stack */
void Push_term (int n) {
    Term *t = new Term;
    t->n_ = n;
    exps.Push_front(t);
}

/* Push a node onto expression stack, top two in stack are its children */
void Push_node (char op) {
    Node *n = new Node;
    n->operation_ = op;
    n->roperator_ = exps.front();
    exps.pop_front();
    n->loperator_ = exps.front();
    exps.pop_front();
    exps.Push_front(n);
}

/*
 * there is only one expression left on the stack, the one that was parsed
 */
void print_exp () {
    Exp *e = exps.front();
    exps.pop_front();
    print_exp(e);
    delete e;
}

La routine suivante peut assez imprimer votre arbre d’expression:

static void
print_exp (Exp *e, std::string ws = "", std::string prefix = "") {
    Term *t = dynamic_cast<Term *>(e);
    if (t) { std::cout << ws << prefix << t->n_ << std::endl; }
    else {
        Node *n = dynamic_cast<Node *>(e);
        std::cout << ws << prefix << "'" << n->operation_ << "'" << std::endl;
        if (prefix.size()) {
            ws += (prefix[1] == '|' ? " |" : "  ");
            ws += "  ";
        }
        print_exp(n->loperator_, ws, " |- ");
        print_exp(n->roperator_, ws, " `- ");
    }
}
3
jxh

J'ai écrit un cours pour gérer cela dans la journée. C'est un peu prolixe et peut-être pas la chose la plus efficace sur la planète mais il gère les entiers signés/non signés, les doubles, les float, les opérations logiques et bit à bit.

Il détecte les dépassements et les dépassements numériques, renvoie un texte descriptif et des codes d'erreur relatifs à la syntaxe, peut être forcé de gérer les doublons sous forme d'entiers, ou d'ignorer la signalisation, prend en charge la précision et l'arrondi intelligents définis par l'utilisateur.

Enfin, ...... il ne repose sur aucune bibliothèque externe, vous pouvez donc simplement le déposer.

Exemple d'utilisation:

CMathParser parser;

double dResult = 0;
int iResult = 0;

//Double math:
if (parser.Calculate("10 * 10 + (6 ^ 7) * (3.14)", &dResult) != CMathParser::ResultOk)
{
    printf("Error in Formula: [%s].\n", parser.LastError()->Text);
}
printf("Double: %.4f\n", dResult);

//Logical math:
if (parser.Calculate("10 * 10 > 10 * 11", &iResult) != CMathParser::ResultOk)
{
    printf("Error in Formula: [%s].\n", parser.LastError()->Text);
}
printf("Logical: %d\n", iResult);

La dernière version est toujours disponible via CMathParser GitHub Repository .

1
NTDLS