C++ 解析c+中的数学表达式+;
我有一个关于解析树的问题: 我有一个字符串(数学表达式),例如:C++ 解析c+中的数学表达式+;,c++,parsing,tree,C++,Parsing,Tree,我有一个关于解析树的问题: 我有一个字符串(数学表达式),例如:(a+b)*c-(d-e)*f/g。我必须在树中解析该表达式: class Exp{}; class Term: public Exp{ int n_; } class Node: Public Exp{ Exp* loperator_; Exp* roperator_; char operation; // +, -, *, / } 我可以使用什么算法来构建表示上述表达式字符串的树?(a+b)*
(a+b)*c-(d-e)*f/g
。我必须在树中解析该表达式:
class Exp{};
class Term: public Exp{
int n_;
}
class Node: Public Exp{
Exp* loperator_;
Exp* roperator_;
char operation; // +, -, *, /
}
我可以使用什么算法来构建表示上述表达式字符串的树?(a+b)*c-(d-e)*f/g
是固定表达式
若要轻松创建一个前缀表达式,请先将其转换为前缀表达式
从这个例子来看,,
(A*B)+(C/D)
的前缀是+(*ab)(/cd)
然后,您的树将has+视为其根节点。您可以继续填充关于每个操作符的左、右子树
此外,还详细解释了递归下降解析,并且可以实现。使用。维基百科的描述相当全面,我希望它足够了
您还可以尝试编写一个正式的语法,例如a,并使用工具生成解析器。列出了3个用于PEG解析的C/C++库。第一步是为表达式编写语法。对于这种简单的情况,第二步是编写递归下降解析器,这是我推荐的算法。这是关于递归下降解析器的wiki页面,它有一个好看的C实现
您可以使用此语法创建表达式
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); }
;
下面是你的lexer
[ \t]+ {}
[0-9]+ { yylval.number = atoi(yytext); return NUMBER; }
[()] { return *yytext; }
[*/+-] { return *yytext; }
表达式在运行时使用以下例程生成:
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;
}
std::list exps;
/*将术语推送到表达式堆栈上*/
无效推送项(整数n){
术语*t=新术语;
t->n=n;
exps.向前推压(t);
}
/*将一个节点推到表达式堆栈上,堆栈中最上面的两个是它的子节点*/
无效推送节点(字符操作){
Node*n=新节点;
n->操作=操作;
n->roperator_uz=exps.front();
exps.pop_front();
n->loperator_u2;=exps.front();
exps.pop_front();
exps.向前推压(n);
}
/*
*堆栈上只剩下一个表达式,即已解析的表达式
*/
无效打印\u exp(){
Exp*e=exps.front();
exps.pop_front();
印刷品(e);;
删除e;
}
以下例程可以漂亮地打印表达式树:
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, " `- ");
}
}
静态无效
打印(exp*e,std::string ws=“”,std::string prefix=“”){
术语*t=动态投影(e);
如果(t){std::cout#包括
#包括
#包括
#包括
#包括
使用名称空间std;
类经验{
公众:
//Exp(){}
虚拟空打印(){}
虚拟void release(){}
};
课程名称:公共实验{
字符串val;
公众:
项(字符串v):val(v){}
作废打印(){
cout=0;--i){
char c=str[i];
如果(c==')'){
++水平;
继续;
}
如果(c=='('){
--水平;
继续;
}
如果(级别>0)继续;
如果(c=='*'| | c=='/')){
左字符串(str.substr(0,i));
右字符串(str.substr(i+1));
返回新节点(c,strToExp(左),strToExp(右));
}
}
如果(str[0]=='('){
//案例()
//从内部拔出并连接至strToExp
对于(int i=0;i我在当时编写了一个类来处理这个问题。它有点冗长,可能不是这个星球上最有效的东西,但它处理有符号/无符号整数、双精度、浮点、逻辑和位运算
它检测数字溢出和下溢,返回有关语法的描述性文本和错误代码,可以强制将双精度作为整数处理,或忽略标志,支持用户定义的精度和智能舍入,甚至在设置DebugMode(true)时显示其工作
最后……它不依赖任何外部库,所以您可以直接将其放入
示例用法:
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);
最新版本总是可以通过。直接翻译步骤有什么帮助?我真的以为OP正在寻找这个。他可以轻松地从前缀表达式构建他的树。他必须解析中缀来构建前缀,所以这根本没有帮助。还要注意,前缀树不需要括号xp最好使用另一个函数。@MooingDuck“prefix tree”:只不过是一个用于打印的表示法。您需要更多的东西吗?我不理解您的第一条评论。我知道prefix tree只是一个表示法,它仍然不需要括号。我相信您的代码在打印时有缺陷“2+5*7”(但我没有测试过,可能是错的)以前从未见过调车场,这是避免RDP递归的聪明方法。OPs问题的备选答案可能重复:显然,该类将处理表达式“10*10*(10*10))“在2.8Ghz的CPU上,每秒大约有500000个表达式。所以,我想,对字符串操作来说还不算太差。完成了!谢谢你的支持!”!
#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 ))
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);