| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 3247 人关注过本帖, 1 人收藏
标题:[原创]一个支持四则运算的类
只看楼主 加入收藏
StarWing83
Rank: 8Rank: 8
来 自:仙女座大星云
等 级:贵宾
威 望:19
帖 子:3951
专家分:748
注 册:2007-11-16
结帖率:90%
收藏(1)
 问题点数:0 回复次数:32 
[原创]一个支持四则运算的类
啊……本来想用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;
}


[[it] 本帖最后由 StarWing83 于 2008-6-14 00:51 编辑 [/it]]
搜索更多相关主题的帖子: 四则运算 算符优先 源码 
2008-05-09 02:53
中学者
Rank: 16Rank: 16Rank: 16Rank: 16
等 级:版主
威 望:20
帖 子:3554
专家分:80
注 册:2007-9-14
收藏
得分:0 
异常抛出最好不要用内建类型,可能会出现捕获的值意义模糊///学习下,准备建立自己的数据结构库///
PS:仅次建议,见笑了..

樱花大战,  有爱.
2008-05-09 07:56
中学者
Rank: 16Rank: 16Rank: 16Rank: 16
等 级:版主
威 望:20
帖 子:3554
专家分:80
注 册:2007-9-14
收藏
得分:0 
没编译器,再漫漫看看

[[it] 本帖最后由 中学者 于 2008-5-9 09:01 编辑 [/it]]

樱花大战,  有爱.
2008-05-09 08:59
秋之爱
Rank: 1
等 级:新手上路
帖 子:23
专家分:0
注 册:2008-4-24
收藏
得分:0 
我 用 c写了 一 个…………
计算器得+,-,*,/都可以用得
还可以用()
2008-05-09 10:34
雨中飛燕
Rank: 1
等 级:新手上路
帖 子:765
专家分:0
注 册:2007-10-13
收藏
得分:0 
我不喜欢用异常,比较喜欢return ERROR_NUMBER

[color=white]
2008-05-09 11:14
StarWing83
Rank: 8Rank: 8
来 自:仙女座大星云
等 级:贵宾
威 望:19
帖 子:3951
专家分:748
注 册:2007-11-16
收藏
得分:0 
异常有个好处,第一个是和返回值完全分开。所以就算是throw 0 都绝不会意义模糊:因为只有你扔异常嘛。然后可以在一个调用树里面随便扔,这样就不需要一层层的if(...)return ....了,最后,异常作为内部的消息传送使用(就是这种啦),再有上面的好处的同时,因为对外封闭,也不太容易看出来。当然,上面的程序不是很好,异常捕获应该在类的内部被封装,而不应该暴露在外面。这个我等会儿改改……
其实异常效率并不算很低。异常的实现方式是注册一个中断向量,然后在throw的时候触发中断。而且如果只是在错误的时候才异常的话,是不会影响到通常工作的效率的~~~

专心编程………
飞燕算法初级群:3996098
我的Blog
2008-05-09 16:11
雨中飛燕
Rank: 1
等 级:新手上路
帖 子:765
专家分:0
注 册:2007-10-13
收藏
得分:0 
那是内存泄露的危机

[color=white]
2008-05-09 16:46
★红狼
Rank: 2
等 级:论坛游民
帖 子:190
专家分:17
注 册:2006-7-12
收藏
得分:0 
[原创]一个支持四则运算的类
啊……本来想用LR文法的,但是在状态量增加到三十多个以后,只好十分沮丧地放弃了………………LR文法版本的还在想办法。先发个随便写写的版本吧……实现的功能很简单,而且自定义运算符的功能还没有测试……


LZ提到 LR 是什么啊.?
四则运算 是不是这样? 1+2*3/4+5-6...这样的连续运算?
2008-05-09 16:53
sunkaidong
Rank: 4
来 自:南京师范大学
等 级:贵宾
威 望:12
帖 子:4496
专家分:141
注 册:2006-12-28
收藏
得分:0 
翅膀兄弟我帮你顶个
我帮你弄个栈,用我们自己的啊..呵呵
#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";
      }

学习需要安静。。海盗要重新来过。。
2008-05-09 16:55
中学者
Rank: 16Rank: 16Rank: 16Rank: 16
等 级:版主
威 望:20
帖 子:3554
专家分:80
注 册:2007-9-14
收藏
得分:0 
[bo]以下是引用 [un]StarWing83[/un] 在 2008-5-9 16:11 的发言:[/bo]

异常有个好处,第一个是和返回值完全分开。所以就算是throw 0 都绝不会意义模糊:因为只有你扔异常嘛。然后可以在一个调用树里面随便扔,这样就不需要一层层的if(...)return ....了,最后,异常作为内部的消息传送使用(就是这种 ...

顶一下,对于我说 的问题,那是因为你即是设计者又是使用者,所以体现不出来....呵呵//

樱花大战,  有爱.
2008-05-09 17:18
快速回复:[原创]一个支持四则运算的类
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.026196 second(s), 7 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved