[原创]一个支持四则运算的类
啊……本来想用LR文法的,但是在状态量增加到三十多个以后,只好十分沮丧地放弃了………………LR文法版本的还在想办法。先发个随便写写的版本吧……实现的功能很简单,而且自定义运算符的功能还没有测试……所以大家帮帮忙来找Bug吧………………注释是后来补的Orz……反正写得很详细啦,大家可以来看看……使用返回值而不是异常返回错误,改掉了几个Bug,明天来分离一目操作符的逻辑,今天就这样吧……
看样子,甚至可以把Sin,Cos这样的函数都加进去呢……
#include <iostream>
#include <string>
#include <stack>
#include <sstream>
using namespace std;
//状态枚举,由主函数及StackCalc返回
enum Status
{
sOK =0,//正常返回
eDivByZero =1,//被零除
eNumError =2,//操作数错误
eOptError =3,//运算符错误
eInvalidToken =4,//非法字符
};
//运算符信息“配件”,用来规定所支持的运算符的数目,优先级,以及计算方法。
//所有“配件”都必须包含以下两个函数
struct CCalcInfo
{
//取得运算符优先级,如不是运算符,返回0,数字越大优先级越低
static int GetOpt(char op)
{
switch (op)
{
case '*':
case '/':return 1;
case '+':
case '-':return 2;
}
return 0;
}
//根据堆栈栈顶的操作数及运算符计算
static Status StackCalc(stack<char>& os,stack<double>& ds)
{
if (os.empty())return eOptError;
if (ds.size() < 2)return eNumError;
double rv=ds.top();ds.pop();
switch (os.top())
{
case '+':ds.top()+=rv;break;
case '-':ds.top()-=rv;break;
case '*':ds.top()*=rv;break;
case '/':if (rv==0)return eDivByZero;ds.top()/=rv;break;
default:return eOptError;
}
os.pop();
return sOK;
}
};
//运算类
template<class TInfo>//TInfo定义了操作符和计算方法
struct CCalculateT : TInfo
{
using TInfo::GetOpt;
using TInfo::StackCalc;
//为方便使用,定义的运算符重载
void operator()(const char* str)
{
double ans;
switch (DoCalc(str,ans))
{
case sOK:cout<<ans;break;
case eDivByZero:cout<<"被零除";break;
case eNumError:cout<<"操作数错误";break;
case eOptError:cout<<"运算符错误";break;
case eInvalidToken:cout<<"非法字符";break;
}
cout<<endl;
}
//运算主程序
static Status DoCalc(const char* str,double& ans)
{
stack<double> ds;//操作数栈
stack<char> os;//符号栈
istringstream iss(str);//输入流
bool bNeedNum=true;//当前是否需要数字(比如操作符和括号后)
for (char ch;iss>>ch;)
{
if (isdigit(ch) || ch=='.')//如果为数字直接入栈
{
double fv;
iss.unget();iss>>fv;
ds.push(fv);//数字入栈
//计算取负操作符
while (!os.empty() && os.top() == '!')
ds.top()=-ds.top(),os.pop();
bNeedNum=false;//不可连续出现两个数字
}
else if (ch == '(')
{
if (bNeedNum == false)return eOptError;//这时应该是期待操作数的
os.push(ch);//括号入栈
bNeedNum=true;//括号后允许出现数字
}
else if (ch == ')')
{
if (bNeedNum == true)return eOptError;//这时应该不期待操作数
//遇到反括号,持续出栈直到遇到相应括号
while (!os.empty() && os.top()!='(')
{
Status s=StackCalc(os,ds);
if (s!=sOK)return s;
}
if (os.empty())return eOptError;//如果找不到相应括号,出错
os.pop();//括号出栈,即消掉了一对括号
//如果有取负操作符,计算
while (!os.empty() && os.top() == '!')
ds.top()=-ds.top(),os.pop();
bNeedNum=false;//反括号后不允许出现数字
}
else if (GetOpt(ch))//如果是操作符
{
//如果不需要出现数字,并且操作符栈非空
//并且栈顶元素不是括号,并且栈顶元素的优先级高于当前运算符
if (!bNeedNum && !os.empty() && GetOpt(os.top())
&& GetOpt(os.top())<=GetOpt(ch))
{
//计算前一计算
Status s=StackCalc(os,ds);
if (s!=sOK)return s;
}
else if (bNeedNum)//如果需要数字,可以认为这时出现的是正负号
{
if (ch == '-')//如果为负号
ch='!';//栈内数字取反,s是自定义的取反操作符
else if (ch == '+')//如果是正号
continue;//无视掉
else return eOptError;//其他字符,出错。
}
bNeedNum=true;//操作符后期待操作数
os.push(ch);//压入操作符
}
else if (!isspace(ch))//非空白字符,出错
return eInvalidToken;
}
while (!os.empty())
{
//计算栈内存留数字,直到操作符栈为空
Status s=StackCalc(os,ds);
if (s != sOK)return s;
}
if (ds.size()!=1) return eNumError;//如果操作数栈内还存有数字,出错。
ans = ds.top();//返回计算结果
return sOK;
}
};
int main(void)
{
char str[1001];
typedef CCalculateT<CCalcInfo> CCalc;//使用四则运算符的运算类
while (putchar('>'),gets(str))CCalc()(str);
return 0;
}
#include <string>
#include <stack>
#include <sstream>
using namespace std;
//状态枚举,由主函数及StackCalc返回
enum Status
{
sOK =0,//正常返回
eDivByZero =1,//被零除
eNumError =2,//操作数错误
eOptError =3,//运算符错误
eInvalidToken =4,//非法字符
};
//运算符信息“配件”,用来规定所支持的运算符的数目,优先级,以及计算方法。
//所有“配件”都必须包含以下两个函数
struct CCalcInfo
{
//取得运算符优先级,如不是运算符,返回0,数字越大优先级越低
static int GetOpt(char op)
{
switch (op)
{
case '*':
case '/':return 1;
case '+':
case '-':return 2;
}
return 0;
}
//根据堆栈栈顶的操作数及运算符计算
static Status StackCalc(stack<char>& os,stack<double>& ds)
{
if (os.empty())return eOptError;
if (ds.size() < 2)return eNumError;
double rv=ds.top();ds.pop();
switch (os.top())
{
case '+':ds.top()+=rv;break;
case '-':ds.top()-=rv;break;
case '*':ds.top()*=rv;break;
case '/':if (rv==0)return eDivByZero;ds.top()/=rv;break;
default:return eOptError;
}
os.pop();
return sOK;
}
};
//运算类
template<class TInfo>//TInfo定义了操作符和计算方法
struct CCalculateT : TInfo
{
using TInfo::GetOpt;
using TInfo::StackCalc;
//为方便使用,定义的运算符重载
void operator()(const char* str)
{
double ans;
switch (DoCalc(str,ans))
{
case sOK:cout<<ans;break;
case eDivByZero:cout<<"被零除";break;
case eNumError:cout<<"操作数错误";break;
case eOptError:cout<<"运算符错误";break;
case eInvalidToken:cout<<"非法字符";break;
}
cout<<endl;
}
//运算主程序
static Status DoCalc(const char* str,double& ans)
{
stack<double> ds;//操作数栈
stack<char> os;//符号栈
istringstream iss(str);//输入流
bool bNeedNum=true;//当前是否需要数字(比如操作符和括号后)
for (char ch;iss>>ch;)
{
if (isdigit(ch) || ch=='.')//如果为数字直接入栈
{
double fv;
iss.unget();iss>>fv;
ds.push(fv);//数字入栈
//计算取负操作符
while (!os.empty() && os.top() == '!')
ds.top()=-ds.top(),os.pop();
bNeedNum=false;//不可连续出现两个数字
}
else if (ch == '(')
{
if (bNeedNum == false)return eOptError;//这时应该是期待操作数的
os.push(ch);//括号入栈
bNeedNum=true;//括号后允许出现数字
}
else if (ch == ')')
{
if (bNeedNum == true)return eOptError;//这时应该不期待操作数
//遇到反括号,持续出栈直到遇到相应括号
while (!os.empty() && os.top()!='(')
{
Status s=StackCalc(os,ds);
if (s!=sOK)return s;
}
if (os.empty())return eOptError;//如果找不到相应括号,出错
os.pop();//括号出栈,即消掉了一对括号
//如果有取负操作符,计算
while (!os.empty() && os.top() == '!')
ds.top()=-ds.top(),os.pop();
bNeedNum=false;//反括号后不允许出现数字
}
else if (GetOpt(ch))//如果是操作符
{
//如果不需要出现数字,并且操作符栈非空
//并且栈顶元素不是括号,并且栈顶元素的优先级高于当前运算符
if (!bNeedNum && !os.empty() && GetOpt(os.top())
&& GetOpt(os.top())<=GetOpt(ch))
{
//计算前一计算
Status s=StackCalc(os,ds);
if (s!=sOK)return s;
}
else if (bNeedNum)//如果需要数字,可以认为这时出现的是正负号
{
if (ch == '-')//如果为负号
ch='!';//栈内数字取反,s是自定义的取反操作符
else if (ch == '+')//如果是正号
continue;//无视掉
else return eOptError;//其他字符,出错。
}
bNeedNum=true;//操作符后期待操作数
os.push(ch);//压入操作符
}
else if (!isspace(ch))//非空白字符,出错
return eInvalidToken;
}
while (!os.empty())
{
//计算栈内存留数字,直到操作符栈为空
Status s=StackCalc(os,ds);
if (s != sOK)return s;
}
if (ds.size()!=1) return eNumError;//如果操作数栈内还存有数字,出错。
ans = ds.top();//返回计算结果
return sOK;
}
};
int main(void)
{
char str[1001];
typedef CCalculateT<CCalcInfo> CCalc;//使用四则运算符的运算类
while (putchar('>'),gets(str))CCalc()(str);
return 0;
}
[[it] 本帖最后由 StarWing83 于 2008-6-14 00:51 编辑 [/it]]