it-swarm.com.de

analysieren des mathematischen Ausdrucks in C++

Ich habe eine Frage zu Parsing Trees:

Ich habe eine Zeichenfolge (math expresion estring), zum Beispiel: (a+b)*c-(d-e)*f/g. Ich muss diesen Ausdruck in einem Baum analysieren:

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

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

Welchen Algorithmus kann ich verwenden, um einen Baum zu erstellen, der den obigen Ausdruck darstellt?

23
Hal Nuevemil

Verwenden Sie den Rangierhof-Algorithmus . Die Wikipedia-Beschreibung ist recht umfangreich, ich hoffe, es reicht aus.

Sie können auch versuchen, eine formale Grammatik zu schreiben, z. B. eine Parsing-Ausdruck-Grammatik und ein Werkzeug zum Generieren eines Parsers verwenden. Diese Seite über PEGs listet 3 C/C++ - Bibliotheken für die PEG-Analyse auf.

10
Kos

(a+b)*c-(d-e)*f/g ist ein fixer Ausdruck. 

Um einfach einen Baum zu erstellen, konvertieren Sie diesen zunächst in einen Präfix-Ausdruck.

Aus dem Beispiel ist Präfix von (A * B) + (C / D)+ (* A B) (/ C D)

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

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

Ihr Baum hat dann + als Wurzelknoten. Sie können den linken und rechten Teilbaum über jeden Operator weiter auffüllen.

Außerdem erklärt this link das rekursive Abstiegs-Parsing im Detail und kann implementiert werden.

5

Der erste Schritt besteht darin, eine Grammatik für Ihre Ausdrücke zu schreiben. Der zweite Schritt für einen so einfachen Fall besteht darin, einen rekursiven Abstiegsparser zu schreiben. Diesen Algorithmus würde ich empfehlen. Hier ist die Wiki-Seite zu rekursiven Abstiegsparsern, die eine gut aussehende C-Implementierung hat.

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

Sie können diese Grammatik verwenden, um Ihren Ausdruck zu erstellen.

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); }
  ;

Und das Folgende für Ihren Lexer.

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

Der Ausdruck wird beim Erstellen mithilfe der folgenden Routinen erstellt:

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;
}

Die folgende Routine kann Ihren Ausdrucksbaum ziemlich gut drucken:

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

Ich habe damals einen Kurs geschrieben, um damit umzugehen. Es ist ein wenig wortreich und vielleicht nicht das effizienteste auf dem Planeten, aber es verarbeitet vorzeichenbehaftete/vorzeichenlose Ganzzahlen, Doppel-, Gleit-, logische und bitweise Operationen.

Es erkennt numerischen Überlauf und Unterlauf, gibt beschreibenden Text und Fehlercodes zur Syntax zurück, kann gezwungen werden, Doppelte als Ganzzahlen zu behandeln oder Beschilderungen zu ignorieren, unterstützt vom Benutzer definierbare Genauigkeit und intelligentes Runden und zeigt sogar seine Arbeit, wenn Sie DebugMode (true) festlegen.

Zu guter Letzt ... es ist nicht auf externe Bibliotheken angewiesen, also können Sie es einfach ablegen.

Verwendungsbeispiel:

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);

Die neueste Version ist immer über das CMathParser GitHub Repository verfügbar.

1
NTDLS