[原创]一个支持四则运算的类
啊……本来想用LR文法的,但是在状态量增加到三十多个以后,只好十分沮丧地放弃了………………LR文法版本的还在想办法。先发个随便写写的版本吧……实现的功能很简单,而且自定义运算符的功能还没有测试……所以大家帮帮忙来找Bug吧………………注释是后来补的Orz……反正写得很详细啦,大家可以来看看……使用返回值而不是异常返回错误,改掉了几个Bug,明天来分离一目操作符的逻辑,今天就这样吧……
看样子,甚至可以把Sin,Cos这样的函数都加进去呢……
[quote][font=新宋体][size=2][color=#FF0000]#include <iostream>
[/color][color=#FF0000]#include <string>
[/color][color=#FF0000]#include <stack>
[/color][color=#FF0000]#include <sstream>
[/color][color=#0000FF]using namespace [/color][color=#FF0000]std[/color];
[color=#008000]//状态枚举,由主函数及StackCalc返回
[/color][color=#0000FF]enum [/color]Status
[color=#800000]{
[/color]sOK =[color=#800080]0[/color],[color=#008000]//正常返回
[/color]eDivByZero =[color=#800080]1[/color],[color=#008000]//被零除
[/color]eNumError =[color=#800080]2[/color],[color=#008000]//操作数错误
[/color]eOptError =[color=#800080]3[/color],[color=#008000]//运算符错误
[/color]eInvalidToken =[color=#800080]4[/color],[color=#008000]//非法字符
[/color][color=#800000]}[/color];
[color=#008000]//运算符信息“配件”,用来规定所支持的运算符的数目,优先级,以及计算方法。
//所有“配件”都必须包含以下两个函数
[/color][color=#0000FF]struct [/color]CCalcInfo
[color=#800000]{
[/color][color=#008000]//取得运算符优先级,如不是运算符,返回0,数字越大优先级越低
[/color][color=#0000FF]static int [/color][color=#008080]GetOpt[/color]([color=#0000FF]char [/color]op)
[color=#800000]{
[/color][color=#0000FF]switch [/color](op)
[color=#800000]{
[/color][color=#0000FF]case [/color][color=#FF8000]'*'[/color]:
[color=#0000FF]case [/color][color=#FF8000]'/'[/color]:[color=#0000FF]return [/color][color=#800080]1[/color];
[color=#0000FF]case [/color][color=#FF8000]'+'[/color]:
[color=#0000FF]case [/color][color=#FF8000]'-'[/color]:[color=#0000FF]return [/color][color=#800080]2[/color];
[color=#800000]}
[/color][color=#0000FF]return [/color][color=#800080]0[/color];
[color=#800000]}
[/color][color=#008000]//根据堆栈栈顶的操作数及运算符计算
[/color][color=#0000FF]static [/color]Status [color=#008080]StackCalc[/color]([color=#FF0000]stack[/color]<[color=#0000FF]char[/color]>& os,[color=#FF0000]stack[/color]<[color=#0000FF]double[/color]>& ds)
[color=#800000]{
[/color][color=#0000FF]if [/color](os.[color=#008080]empty[/color]())[color=#0000FF]return [/color]eOptError;
[color=#0000FF]if [/color](ds.[color=#FF8000]size[/color]() < [color=#800080]2[/color])[color=#0000FF]return [/color]eNumError;
[color=#0000FF]double [/color]rv=ds.[color=#008080]top[/color]();ds.[color=#FF8000]pop[/color]();
[color=#0000FF]switch [/color](os.[color=#008080]top[/color]())
[color=#800000]{
[/color][color=#0000FF]case [/color][color=#FF8000]'+'[/color]:ds.[color=#008080]top[/color]()+=rv;[color=#0000FF]break[/color];
[color=#0000FF]case [/color][color=#FF8000]'-'[/color]:ds.[color=#008080]top[/color]()-=rv;[color=#0000FF]break[/color];
[color=#0000FF]case [/color][color=#FF8000]'*'[/color]:ds.[color=#008080]top[/color]()*=rv;[color=#0000FF]break[/color];
[color=#0000FF]case [/color][color=#FF8000]'/'[/color]:[color=#0000FF]if [/color](rv==[color=#800080]0[/color])[color=#0000FF]return [/color]eDivByZero;ds.[color=#008080]top[/color]()/=rv;[color=#0000FF]break[/color];
[color=#0000FF]default[/color]:[color=#0000FF]return [/color]eOptError;
[color=#800000]}
[/color]os.[color=#FF8000]pop[/color]();
[color=#0000FF]return [/color]sOK;
[color=#800000]}
}[/color];
[color=#008000]//运算类
[/color][color=#0000FF]template[/color]<[color=#0000FF]class [/color]TInfo>[color=#008000]//TInfo定义了操作符和计算方法
[/color][color=#0000FF]struct [/color]CCalculateT : TInfo
[color=#800000]{
[/color][color=#0000FF]using [/color]TInfo::GetOpt;
[color=#0000FF]using [/color]TInfo::StackCalc;
[color=#008000]//为方便使用,定义的运算符重载
[/color][color=#0000FF]void operator[/color]()([color=#0000FF]const char[/color]* str)
[color=#800000]{
[/color][color=#0000FF]double [/color]ans;
[color=#0000FF]switch [/color]([color=#008080]DoCalc[/color](str,ans))
[color=#800000]{
[/color][color=#0000FF]case [/color]sOK:[color=#FF0000]cout[/color]<<ans;[color=#0000FF]break[/color];
[color=#0000FF]case [/color]eDivByZero:[color=#FF0000]cout[/color]<<[color=#FF00FF]"被零除"[/color];[color=#0000FF]break[/color];
[color=#0000FF]case [/color]eNumError:[color=#FF0000]cout[/color]<<[color=#FF00FF]"操作数错误"[/color];[color=#0000FF]break[/color];
[color=#0000FF]case [/color]eOptError:[color=#FF0000]cout[/color]<<[color=#FF00FF]"运算符错误"[/color];[color=#0000FF]break[/color];
[color=#0000FF]case [/color]eInvalidToken:[color=#FF0000]cout[/color]<<[color=#FF00FF]"非法字符"[/color];[color=#0000FF]break[/color];
[color=#800000]}
[/color][color=#FF0000]cout[/color]<<[color=#FF0000]endl[/color];
[color=#800000]}
[/color][color=#008000]//运算主程序
[/color][color=#0000FF]static [/color]Status [color=#008080]DoCalc[/color]([color=#0000FF]const char[/color]* str,[color=#0000FF]double[/color]& ans)
[color=#800000]{
[/color][color=#FF0000]stack[/color]<[color=#0000FF]double[/color]> ds;[color=#008000]//操作数栈
[/color][color=#FF0000]stack[/color]<[color=#0000FF]char[/color]> os;[color=#008000]//符号栈
[/color]istringstream [color=#008080]iss[/color](str);[color=#008000]//输入流
[/color][color=#0000FF]bool [/color]bNeedNum=true;[color=#008000]//当前是否需要数字(比如操作符和括号后)
[/color][color=#0000FF]for [/color]([color=#0000FF]char [/color]ch;iss>>ch;)
[color=#800000]{
[/color][color=#0000FF]if [/color]([color=#008080]isdigit[/color](ch) || ch==[color=#FF8000]'.'[/color])[color=#008000]//如果为数字直接入栈
[/color][color=#800000]{
[/color][color=#0000FF]double [/color]fv;
iss.[color=#008080]unget[/color]();iss>>fv;
ds.[color=#FF8000]push[/color](fv);[color=#008000]//数字入栈
//计算取负操作符
[/color][color=#0000FF]while [/color](!os.[color=#008080]empty[/color]() && os.[color=#008080]top[/color]() == [color=#FF8000]'!'[/color])
ds.[color=#008080]top[/color]()=-ds.[color=#008080]top[/color](),os.[color=#FF8000]pop[/color]();
bNeedNum=false;[color=#008000]//不可连续出现两个数字
[/color][color=#800000]}
[/color][color=#0000FF]else if [/color](ch == [color=#FF8000]'('[/color])
[color=#800000]{
[/color][color=#0000FF]if [/color](bNeedNum == false)[color=#0000FF]return [/color]eOptError;[color=#008000]//这时应该是期待操作数的
[/color]os.[color=#FF8000]push[/color](ch);[color=#008000]//括号入栈
[/color]bNeedNum=true;[color=#008000]//括号后允许出现数字
[/color][color=#800000]}
[/color][color=#0000FF]else if [/color](ch == [color=#FF8000]')'[/color])
[color=#800000]{
[/color][color=#0000FF]if [/color](bNeedNum == true)[color=#0000FF]return [/color]eOptError;[color=#008000]//这时应该不期待操作数
//遇到反括号,持续出栈直到遇到相应括号
[/color][color=#0000FF]while [/color](!os.[color=#008080]empty[/color]() && os.[color=#008080]top[/color]()!=[color=#FF8000]'('[/color])
[color=#800000]{
[/color]Status s=[color=#008080]StackCalc[/color](os,ds);
[color=#0000FF]if [/color](s!=sOK)[color=#0000FF]return [/color]s;
[color=#800000]}
[/color][color=#0000FF]if [/color](os.[color=#008080]empty[/color]())[color=#0000FF]return [/color]eOptError;[color=#008000]//如果找不到相应括号,出错
[/color]os.[color=#FF8000]pop[/color]();[color=#008000]//括号出栈,即消掉了一对括号
//如果有取负操作符,计算
[/color][color=#0000FF]while [/color](!os.[color=#008080]empty[/color]() && os.[color=#008080]top[/color]() == [color=#FF8000]'!'[/color])
ds.[color=#008080]top[/color]()=-ds.[color=#008080]top[/color](),os.[color=#FF8000]pop[/color]();
bNeedNum=false;[color=#008000]//反括号后不允许出现数字
[/color][color=#800000]}
[/color][color=#0000FF]else if [/color]([color=#008080]GetOpt[/color](ch))[color=#008000]//如果是操作符
[/color][color=#800000]{
[/color][color=#008000]//如果不需要出现数字,并且操作符栈非空
//并且栈顶元素不是括号,并且栈顶元素的优先级高于当前运算符
[/color][color=#0000FF]if [/color](!bNeedNum && !os.[color=#008080]empty[/color]() && [color=#008080]GetOpt[/color](os.[color=#008080]top[/color]())
&& [color=#008080]GetOpt[/color](os.[color=#008080]top[/color]())<=[color=#008080]GetOpt[/color](ch))
[color=#800000]{
[/color][color=#008000]//计算前一计算
[/color]Status s=[color=#008080]StackCalc[/color](os,ds);
[color=#0000FF]if [/color](s!=sOK)[color=#0000FF]return [/color]s;
[color=#800000]}
[/color][color=#0000FF]else if [/color](bNeedNum)[color=#008000]//如果需要数字,可以认为这时出现的是正负号
[/color][color=#800000]{
[/color][color=#0000FF]if [/color](ch == [color=#FF8000]'-'[/color])[color=#008000]//如果为负号
[/color]ch=[color=#FF8000]'!'[/color];[color=#008000]//栈内数字取反,s是自定义的取反操作符
[/color][color=#0000FF]else if [/color](ch == [color=#FF8000]'+'[/color])[color=#008000]//如果是正号
[/color][color=#0000FF]continue[/color];[color=#008000]//无视掉
[/color][color=#0000FF]else return [/color]eOptError;[color=#008000]//其他字符,出错。
[/color][color=#800000]}
[/color]bNeedNum=true;[color=#008000]//操作符后期待操作数
[/color]os.[color=#FF8000]push[/color](ch);[color=#008000]//压入操作符
[/color][color=#800000]}
[/color][color=#0000FF]else if [/color](![color=#008080]isspace[/color](ch))[color=#008000]//非空白字符,出错
[/color][color=#0000FF]return [/color]eInvalidToken;
[color=#800000]}
[/color][color=#0000FF]while [/color](!os.[color=#008080]empty[/color]())
[color=#800000]{
[/color][color=#008000]//计算栈内存留数字,直到操作符栈为空
[/color]Status s=[color=#008080]StackCalc[/color](os,ds);
[color=#0000FF]if [/color](s != sOK)[color=#0000FF]return [/color]s;
[color=#800000]}
[/color][color=#0000FF]if [/color](ds.[color=#FF8000]size[/color]()!=[color=#800080]1[/color]) [color=#0000FF]return [/color]eNumError;[color=#008000]//如果操作数栈内还存有数字,出错。
[/color]ans = ds.[color=#008080]top[/color]();[color=#008000]//返回计算结果
[/color][color=#0000FF]return [/color]sOK;
[color=#800000]}
}[/color];
[color=#0000FF]int [/color][color=#FF0000]main[/color]([color=#0000FF]void[/color])
[color=#800000]{
[/color][color=#0000FF]char [/color]str[color=#800000][[/color][color=#800080]1001[/color][color=#800000]][/color];
[color=#0000FF]typedef [/color]CCalculateT<CCalcInfo> CCalc;[color=#008000]//使用四则运算符的运算类
[/color][color=#0000FF]while [/color]([color=#FF0000]putchar[/color]([color=#FF8000]'>'[/color]),[color=#FF0000]gets[/color](str))[color=#008080]CCalc[/color]()(str);
[color=#0000FF]return [/color][color=#800080]0[/color];
[color=#800000]}
[/color][/size][/font][/quote]
[[it] 本帖最后由 StarWing83 于 2008-6-14 00:51 编辑 [/it]]
PS:仅次建议,见笑了..[tk03] 没编译器,再漫漫看看
[[it] 本帖最后由 中学者 于 2008-5-9 09:01 编辑 [/it]] 我 用 c写了 一 个…………
计算器得+,-,*,/都可以用得
还可以用() 我不喜欢用异常,比较喜欢return ERROR_NUMBER
[img]http://blog.programfan.com/upfile/200804/20080430094836.gif[/img][color=white] 异常有个好处,第一个是和返回值完全分开。所以就算是throw 0 都绝不会意义模糊:因为只有你扔异常嘛。然后可以在一个调用树里面随便扔,这样就不需要一层层的if(...)return ....了,最后,异常作为内部的消息传送使用(就是这种啦),再有上面的好处的同时,因为对外封闭,也不太容易看出来。当然,上面的程序不是很好,异常捕获应该在类的内部被封装,而不应该暴露在外面。这个我等会儿改改……
其实异常效率并不算很低。异常的实现方式是注册一个中断向量,然后在throw的时候触发中断。而且如果只是在错误的时候才异常的话,是不会影响到通常工作的效率的~~~ 那是内存泄露的危机
[img]http://blog.programfan.com/upfile/200804/20080430094836.gif[/img][color=white] [原创]一个支持四则运算的类
啊……本来想用LR文法的,但是在状态量增加到三十多个以后,只好十分沮丧地放弃了………………LR文法版本的还在想办法。先发个随便写写的版本吧……实现的功能很简单,而且自定义运算符的功能还没有测试……
LZ提到 LR 是什么啊.?
四则运算 是不是这样? 1+2*3/4+5-6...这样的连续运算?
翅膀兄弟我帮你顶个
我帮你弄个栈,用我们自己的啊..呵呵#include<iostream>
#include<vector>
#include<string>
using namespace std;
template <class T>
class istack
{
public:
istack( int cap ): _capacity( cap ), _top(0) {}
bool pop();
bool push(T value);
bool full();
bool empty();
void display();
int size();
private:
vector<T>::size_type _top;
int _capacity;
vector<T> _stack;
};
template <class T>
inline int istack<T>::size(){return _capacity;};
template <class T>
inline bool istack<T>::empty()
{return _top?false:true;}
template <class T>
inline bool istack<T>::full()
{return _top<size()-1?false:true;}
template <class T>
bool istack<T>::pop()
{
if(empty())
return false;
T i=_stack.at(_top-1);
_stack.pop_back();
cout<<"istack:pop():"<<i<<endl;
_top--;
return true;
}
template <class T>
bool istack<T>::push(T value)
{
if(full())
return false;
_top++;
_stack.push_back(value);
cout<<"istack::push()("<<value<<")\n";
return true;
}
template <class T>
void istack<T>::display()
{
if(!size())
{cout<<"(0)\n";return;}
cout<<"("<<size()<<")(bot:";
for(vector<T>::size_type ix=_top-1;ix>0;ix--)
cout<<_stack.at(ix)<<" ";
cout<<":top)\n";
} [quote][bo]以下是引用 [un]StarWing83[/un] 在 2008-5-9 16:11 的发言:[/bo]
异常有个好处,第一个是和返回值完全分开。所以就算是throw 0 都绝不会意义模糊:因为只有你扔异常嘛。然后可以在一个调用树里面随便扔,这样就不需要一层层的if(...)return ....了,最后,异常作为内部的消息传送使用(就是这种 ... [/quote]
顶一下,对于我说 的问题,那是因为你即是设计者又是使用者,所以体现不出来....呵呵// 不,中学者我知道你的意思。你的意见是对的,直接扔int的确含义模糊。我正在改。我的想法是只是在类内部扔异常。这个异常的过程对使用者来说是透明的,他只需要cout<<CCalc("12+(12+4)");。至于是错误还是结果交给类来处理了……
飞燕是什么意思呢?不是很明白……扔异常本身会造成内存泄露吗?如果是的话我就改成返回值好了……因为设计了接口,如果要返回值的话会很麻烦…… 抛异常会造成内存泄露的啊~~~ 中学为什么?会有内存泄露..如果说是因为用new 产生对象返回指针,没有释放好理解...如果不是动态创建怎么泄露呢? 是啊,不太明白,我已经没有改了,中学者先说说,估计会取消掉异常机制了……哎,那样的话接口机制也不能用了——因为要跨三层函数返回错误…… 异常的思路无非是下面:
一,设计异常类,把可能错误都包括在异常里面;excption ;
二,设计正常类,当发现数据违例的时候,用throw exception()(1);
三,在正常使用过程try{}catch( exception e){}(2);
1和2的关系是什么?是引用还是副本?传递机制是什么呢?知道的说下..呵呵 在函数内发生异常可以在两个地方处理,一个是throw然后在函数外的try块catch捕获;另一种直接在函数体内设置try块catch捕获.....往往可能出现内存泄露的就是第一种---通过throw的方式..
一旦throw,则直接跳出函数体,这个时候会释放函数体内的对象(如果对象尚未构建完成则不会析构释放,这种往往是自己重写new的时候会发生).
下面来段小代码可以看出内存泄露了(实际编程肯定不会这样写):
[code]
class cat{
public:
cat(){ cout<<"creat cat"<<endl;}
~cat(){cout<<"destroy cat"<<endl;}
};
class fate
{
cat* p;
public:
fate():p(0){ p=new cat[5]; throw 2; }
~fate() { delete [] p; }
};
int main(void)
{
try{
fate f;
}
catch(int){
cout<<"oh my god!"<<endl;
}
return 0;
}
[/code] 今天刚好搞异常,sunkai兄的那个是副本....所以用异常我认为也有效率的考虑~ 晕..呵呵..忘记异常是强行打破正常执行顺序了...后面的执行过程都被异常屏蔽了..java用习惯了...没注意这个问题 现在在开始写自己的DataStruction ,慢慢更新///呵呵[tk05] case 0:cout<<"被零除";break; //这条语句一定可以捕捉么?
页:
[1]
2
