按照老规矩,应该是先放示例图
简单介绍后序表达式
一般我们以前数学课学习的数学表达式都是中序的,比如说一加一,则写法就是1+1
。
而在计算的时候,为了方便编写数学表达式求值的计算机程序,通常会输入后序表达式。如1+1
的后缀表达式为1 1 -
。
举个更复杂的例子,比如今有中序表达式:
1 + (1 - 2) / 3
则它的后序表达式为:
1 2 - 3 / 1 +
为什么后序表达式会更方便编写计算的算法呢?
我们使用数据结构 - 栈 来进行我们的算法。至于什么是栈,请咨询数据结构老师吧。
将上述例子储存到栈中是这个样子的:
图中使用了两个栈,一个用来储存后序表达式,一个用来放置我们的计算结果。
后序表达式求值算法
话不多说,直接看图吧,每一个图表示一个步骤。
将中序表达式转换为后序表达式
那么我们的目标很明确了,就是编写一个算法,输入是中序表达式字符串,输出是后序表达式栈,之后再使用上述后序表达式求值算法,即可求出表达式的值。
递归下降语法分析器
该语法分析器会接受一个字符串输入,然后根据定义的数学表达式,输出后序表达式栈。
定义数学表达式
_Expr -> _Term0 | _Expr + _Term0 | _Expr - _Term0
_Term0 -> _Term1 | _Term0 * _Term1 | _Term0 / _Term1
_Term1 -> _Term2 | _Term1 ^ _Term2
_Term2 -> _Real | ( _Expr ) | _Func ( _Args )
_Real -> _Positive | - _Positive | + _Positive
_Positive -> _Interger | _Interger . _Interger
_Interger -> digital | _Interger digital
_Func -> letter | _Func letter | _Func digital
_Args -> _Expr | _Args , _Expr
根据定义编写函数
_Expr
//表达式
void _Expr()
{
_Term0();
while(true)
{
//在0级优先级的操作符中搜索是否有这个操作符
auto find_opt = opts_0.find(*next);
if(find_opt != string::npos)
{
auto op = opts_0[find_opt];
_Match(op);
_Term0();
output += op;
LexerValue lex;
lex.o = op;
theExp.exp.push(make_pair(LexerType::OPT, lex));
}else if(*next == '.')
{
cout<<"Too many '.'!"<<endl;
throw theExp;
}else
{
return;
}
}
}
_Term0
//0级优先级二元操作符
void _Term0()
{
_Term1();
while(true)
{
//在1级优先级的操作符中搜索是否有这个操作符
auto find_opt = opts_1.find(*next);
if(find_opt != string::npos)
{
auto op = opts_1[find_opt];
_Match(op);
_Term1();
output += op;
LexerValue lex;
lex.o = op;
theExp.exp.push(make_pair(LexerType::OPT, lex));
}else
{
return;
}
}
}
_Term1
//1级优先级二元操作符
void _Term1()
{
_Term2();
while(true)
{
//在2级优先级的操作符中搜索是否有这个操作符
auto find_opt = opts_2.find(*next);
if(find_opt != string::npos)
{
auto op = opts_2[find_opt];
_Match(op);
_Term2();
output += op;
LexerValue lex;
lex.o = op;
theExp.exp.push(make_pair(LexerType::OPT, lex));
}else
{
return;
}
}
}
_Term2
//2级优先级二元操作符
void _Term2()
{
if(IsDigit(*next) || (next + 1 != end && (*next == '-' || *next == '+') && IsDigit(*(next + 1))))
{
output += '<';
auto b = output.size();
_Real();
string real(output.begin() + b, output.end());
LexerValue lex;
lex.r = StrToReal(real);
theExp.exp.push(make_pair(LexerType::REAL, lex));
output += '>';
}else if(*next == '(')
{
_Match('(');
_Expr();
_Match(')');
}else if(IsLetter(*next))
{
auto b = output.size();
_Func();
string func_name(output.begin() + b, output.end());
output += '(';
_Match('(');
_Args();
_Match(')');
output += ')';
LexerValue lex;
lex.f = func_name;
theExp.exp.push(make_pair(LexerType::FUNC, lex));
}
}
_Real
//实数
void _Real()
{
if(*next == '-')
{
_Match('-');
output += '-';
_Positive();
}if(*next == '+')
{
_Match('+');
_Positive();
}
else
{
_Positive();
}
}
_Positive
//正数
void _Positive()
{
_Interger();
if(*next == '.')
{
output += '.';
_Match('.');
_Interger();
}
}
_Interger
//正整数
void _Interger()
{
while(IsDigit(*next))
{
output += *next;
_Match(*next);
}
}
_Func
//函数名
void _Func()
{
if(IsLetter(*next))
{
char l = *next;
output += l;
_Match(l);
while(IsLetter(*next) || IsDigit(*next))
{
output += *next;
_Match(*next);
}
}
}
_Args
//函数的参数
void _Args()
{
while(true)
{
_Expr();
if(__Match(','))
{
output += ',';
}else
{
break;
}
};
}
匹配函数
bool __Match(char t)
{
while(*next == ' ')++next;
if(*next == t)
{
do{
++next;
}while(*next == ' ');
return true;
}
return false;
}
void _Match(char t)
{
if(!__Match(t))
{
theExp.exp = stack<Lexer>();
cout<<"nMatch ( '"<<t<<"' with '"<<*next<<"' ) fail!"<<endl;
throw theExp;
return;
}
}
运行示例
该程序不但能处理整数,还可以处理负数,浮点数以及在力所能及的范围内给出错误参考提示。
我在定义数学表达式时让其支持根据函数名调用,可以实现一下奇怪的功能。比如下面这个设定随机数种子并获取一个0到99内的随机数。
完整源代码
https://github.com/rayxuln/CodingNotebook/blob/master/ExpCalculator.cpp
参考资料
[1] 编译原理