今天看到一篇表达式求值的文章,想起大一上数据结构课的时候数据结构老师让我们编写表达式计算的代码一直都没写出来。现在觉得该出一口气了。
/*-------------------Expression Caculator----------------
Method:把运算符中续变成后续
如:23+56/(102-100)*((36-24)/(8-6))
转换成:23|56|102|100|-|/|*|36|24|-|8|6|-|/|*|+
在栈中上面的后续表达式是颠倒着存放的。
把栈顶的数字取出放在另一个栈里面。如果遇到运算符就用它计算数字栈的前两个数字。
算法参考:CSDN-vbprog的专栏-字符串表达式计算C#程序设计
Node:
不支持多于两个'-'连续;
不支持一元运算符('-A','!A'是通过换算成'(0-A)','(0!A)'来计算的);
不支持长度大于一的运算符;
不支持函数;
返回类型不符合最大原则(一律float型);
支持如下运算符:
+,-:加减运算;
&,|:逻辑与或;
*,/,%:乘除求余运算(其中求余先被转换成long);
!,^:逻辑非和乘方运算
(,):括号
上面的运算符优先级由低到高。
总体评价:
由于不支持函数和长运算符,扩充功能受限;
没有采用面向对象的技术,不利于管理和优化;
由于把取优先级函数、计算器跟分析器分开,使二元运算符很容易扩充。
总结:
检查器,分析器,计算器,汇算器 设计成类(或者计算器采取函数指针,利用库自带的重载操作符);
运算符,操作数设置成类;
修正上面Node和评价中的缺点和错误。
*/
#include <iostream>
#include <string>
#include <stack>
#include <math.h>
struct Err{
string disc;
Err(const string &s){disc="Er>"+s;}
};//自定义异常类型
int GetPro(char s){
switch(s){
case '+':
case '-':
return 1;
case '&':
case '|':
return 2;
case '*':
case '/':
case '%':
return 3;
case '!':
return 4;
case '^':
return 5;
default:
return -1;
}
}//GetPro:Get prorioty of operators. return -1 if s isn't an operators
float CacuTwo(const float& fa,const float& fb,const char &oper){
switch(oper){
case '+':
return fa+fb;
case '-':
return fa-fb;
case '&':
return fa&&fb;
case '|':
return fa||fb;
case '*':
return fa*fb;
case '/':
return fa/fb;
case '%':
return long(fa)%long(fb);
case '!':
return !fb;
case '^':
return pow(fa,fb);
default:
return 0;
}
} //计算器:计算两个数进行的计算。
int CheckExp(string &s){
try{
int dot; //一个数中有不止一个小数点
int br=0;//括号检查
int brs=-1,bre=-1; //括号开始和结束标号(分别指向左括号和右括号)
int ns=-1,ne=-1; //数字开始和结束标号(分别指向一个数字的开始和结束)
for(int i=0;i<s.length();i++){
ns=ne=-1;
if(GetPro(s.at(i))>0)continue; //是运算符
if(isdigit(s.at(i))){
ns=i; //数字开始
dot=0; //小数点数
while(isdigit(s.at(i))||s.at(i)=='.'){
if(s.at(i)=='.'&&dot>0)throw Err("多个'.'在数字中!");
if(s.at(i)=='.')dot++;
i++;
if(i==s.length())break;
}
i--;
ne=i;
if(ns>0){ //检查'-'和'!'单目运算符,并换成相应的双目式
if(s.at(ns-1)=='-'){
if(ns>1){
if(s.at(ns-2)=='('||GetPro(s.at(ns-2))>0){
s.replace(ns-1,2+ne-ns,"(0-"+s.substr(ns,ne-ns+1)+")");
i+=3;
}
}
else {
s.replace(0,2+ne-ns,"(0-"+s.substr(ns,ne-ns+1)+")");
i+=3;
}
}
if(s.at(ns-1)=='!'){
if(ns>1){
if(s.at(ns-2)=='('||GetPro(s.at(ns-2))>0){
s.replace(ns-1,2+ne-ns,"(0!"+s.substr(ns,ne-ns+1)+")");
i+=3;
}
}
else {
s.replace(0,2+ne-ns,"(0!"+s.substr(ns,ne-ns+1)+")");
i+=3;
}
}
}
continue;
}
if(s.at(i)=='('){ //检查左括号
br++;
brs=i;
if(i>0){
if(isdigit(s.at(i)))throw Err(s.at(i)+"'('错误!");
}
continue;
}
if(s.at(i)==')'){ //检查右括号
br--;
if(br<0)throw Err("括号不匹配!");
bre=i;
if(brs>0){ //检查'-'和'!'单目运算符,并换成相应的双目式
if(s.at(brs-1)=='-'){
if(brs>1){
if(s.at(brs-2)=='('||GetPro(s.at(brs-2))>0){
s.replace(brs-1,2+bre-brs,"(0-"+s.substr(brs,bre-brs+1)+")");
i+=3;
}
}
else{
s.replace(0,2+bre-brs,"(0-"+s.substr(brs,bre-brs+1)+")");
i+=3;
}
}
if(s.at(brs-1)=='!'){
if(brs>1){
if(s.at(brs-2)=='('||GetPro(s.at(brs-2))>0){
s.replace(brs-1,2+bre-brs,"(0!"+s.substr(brs,bre-brs+1)+")");
i+=3;
}
}
else{
s.replace(0,2+bre-brs,"(0!"+s.substr(brs,bre-brs+1)+")");
i+=3;
}
}
}
continue;
}
}
if(br!=0)throw Err("括号不匹配!");
return 1;
}
catch(Err e){
cerr<<e.disc<<endl;
return 0;
}
}//检验器
/*
int atofx(const string &t,float &s){
try{
if(t.length()==0)return 0;
int dot=0,bgr0=0;
for(int i=0;i<t.size();i++){
if(isdigit(t.at(i))||t.at(i)=='.'){
if(t.at(i)>'0')bgr0=1;
if(t.at(i)=='.')dot++;
}
else throw Err("表达式错误!");
}
if(dot>1)throw Err("错误的数字: 两个点?");
float rt=atof(t.c_str());
if(rt==0&&bgr0>0)throw Err("表达式错误!");
s=rt;
return 1;
}
catch(Err e){
cerr<<e.disc<<endl;
return 0;
}
} */
int Read(const string &t,stack<string> &operands){
try{
stack<char> opers;
string operbuf="";
for(int i=0;i<t.length();i++){
operbuf="";
if(isdigit(t.at(i))){
while(isdigit(t.at(i))||t.at(i)=='.'){
if(t.at(i)=='.'&&operbuf.find_first_of('.')>=0&&operbuf.find_first_of('.')<=operbuf.length()){
throw Err("错误的字符:.");
}
operbuf+=t.at(i++);
if(i==t.length())break;
}
operands.push(operbuf);
if(i==t.length())break;
i--;
}//Dill numbers
if(t.at(i)=='('){
opers.push('(');
} //Dill '('
operbuf="";
if(GetPro(t.at(i))>0){
if(opers.size()>0){
if(opers.top()=='('||GetPro(opers.top())<GetPro(t.at(i))){
opers.push(t.at(i));
}
else{
operbuf=opers.top();
operands.push(operbuf);
opers.pop();
opers.push(t.at(i));
}
}
else opers.push(t.at(i));
} //Dill operators
operbuf="";
if(t.at(i)==')'){
while(opers.top()!='('){
operbuf=opers.top();
operands.push(operbuf);
opers.pop();
}
opers.pop();
}
}
while(opers.size()>0){
operbuf=opers.top();
operands.push(operbuf);
opers.pop();
}
int i=operands.size(),j=operands.size();
string temp[i];
for(i--;i>=0;i--){
temp[i]=operands.top();
operands.pop();
//cout<<temp[i]<<"|";
}
//cout<<endl;
for(i=j-1;i>=0;i--){
operands.push(temp[i]);
}
return 1;
}
catch(Err e){
while(operands.size()>0)operands.pop();
cerr<<e.disc<<endl;
return 0;
}
catch(...){
cerr<<"Er>Unknown Error Occured! So sorry!"<<endl;
}
}
float Caculate(stack<string> &operands){
try{
float numa,numb;
stack<float> nums;
while(operands.size()>0){
if(isdigit(operands.top().at(0))){
float s;
//if(atofx(operands.top().c_str(),s)==0)return 0;
s=atof(operands.top().c_str());
nums.push(s);
}
else{
if(nums.size()==0)throw Err(operands.top()+"缺少对象!");
numa=nums.top();
nums.pop();
numb=nums.top();
nums.pop(); //从数字栈中取出两个数
nums.push(CacuTwo(numb,numa,operands.top().at(0))); //计算后再放到数字栈
}
operands.pop();
}
if(nums.size()!=1){throw Err("缺少运算符!");}
if(operands.size()!=0){throw Err("缺少运算符!");}
return nums.top();
}
catch(Err e){
while(operands.size()>0)operands.pop();
cerr<<e.disc<<endl;
return 0;
}
catch(...){
cerr<<"Er>Unknown Error"<<endl;
}
}
void main(){
try{
stack<string>operands;
string s;
for(;;){
cout<<"Ex>";
cin>>s;
if(s=="exit")break;
if(CheckExp(s)!=1){
continue;
}
while(operands.size()>0)operands.pop();
Read(s,operands);
cout<<"Re>"<<Caculate(operands)<<endl;
}
}
catch(...){
cout<<"Unknown Error Occured! So sorry!"<<endl;
}
}