Helix浅谈算式处理(C++计算器源码发布)

来源:互联网 发布:淘宝加盟诈骗案进展 编辑:程序博客网 时间:2024/05/29 13:08
最近一直在研究计算器,前段时间也算写出一个小有成果的版本,姑且命名为Helix SStrict Calculator Beta(HSSCalcBeta)吧,之后还会以面向对象的新思路写出更好的版本(往语法分析技术靠近)。

如果有什么不合理的地方,还请多多批评指教。

另附程序猿交流QQ群:532320075,在这个充满理想与奋斗的家,我们期待着您的加入^▽^。

目前计算器允许输入+-*/(),每一个操作符左右必须接受两个值(数字或计算出值的操作符),每一个值(包括括号)必须属于一个操作符。
所以尤其是-1,((1+2)),(1)都会报错。


二叉树:

来分析一下算式处理的原理吧。
我们发现,每一个操作符都以某种格式接受1个或n个值,然后运算返回一个值。
这样一来就形成了一种类似树状的结构。
我们弱化一下,让操作符接受2个值,比如+-*/,多叉树就变成了二叉树。


优先级处理:

然后就是优先级处理,就像小学数学教的乘除比加减优先一样。
例如5+3*2按原来应该先算5+3=8,然后再算8*2=16,但是由于*的优先级比+高,所以变成了3*2=6,5+6=11。
优先级处理示意图
如图,这就是在同一个算式串(括号)中出现两个运算符时的建树策略。


括号处理:

再到括号,括号是一个比较麻烦的东西,也是使这个计算器制作得不完善的最大因素(在未来的版本中,会作为两块单独的token处理)。
在当前的版本中,我们在最开始读入算式串时就对括号嵌套检查一遍,降低处理难度。
将括号的标记(inBracket)交给这个括号里优先级最低的运算符,这样就可以把这棵子树圈住,正确定位了。

没有扫描到 ( 时,就进行普通的优先级处理。
扫描到时,就建立子节点,并将输入处理交给子节点(接下来的输入是括号内的内容)。
扫描到 ) 时,就把输入处理返还给父节点。
以上就是大致的原理。


综上所述:

下图是测试用的算式串,我已经把建树的方式画出来,希望能帮助大家理解。
为了加快读取效率,我们先顺向读取建树,读到需要优先级处理时再回头纠错。
线上的字符代表因读取到这个字符而做的动作(或写入给某个元素)。
绿线代表读取到操作符并赋值给自身。
灰线代表因优先级处理而被断开的关系。
棕线代表因优先级处理而被连接上的关系。
建树示意图


建树策略:

根据一个节点是否有左子节点 操作符 右子节点分为stage left,stage oper,stage right,还有检查后面是否有 ) 或需要优先级处理的操作符的stage ending。
然后再根据输入是数字 操作符 括号或结束符编成状态机,进行建树。
再从root节点递归向下请求值,就能计算出结果了。
编成的实际有效代码约350行。

源码下载地址:
HSSCalcBeta.cpp
http://download.csdn.net/detail/fshelix/9764518


实际代码如下:

#include <iostream>#include <sstream>#include <vector>using namespace std;const int row=3,col=2;const int INVALID=-2;const int LOWER=-1,HIGHER=1;const int STGLEFT=2,STGOPER=3,STGRIGHT=4,STGENDING=5;const char operTable[row][col]= {//允许的字符     {'+','-'},    {'*','/'},    {'(',')'}};class node {//建树用的节点元素,保存有操作符 数字 是否根节点 是否被括号直属包围 以及节点之间的关系     public:        node();        void setleft(node *l) {//同时处理父关系             left=l;            l->father=this;            l->isleft=true;        }        void setright(node *r) {            right=r;            r->father=this;            r->isleft=false;        }        void setinbrac(bool b) {            inbracket=b;        }        void setisroot(bool r) {            isroot=r;        }        void setoper(char o) {            isoper=true;            oper=o;        }        void setnum(int n) {            isoper=false;            num=n;        }        bool getinbrac() {            return inbracket;        }        bool getisroot() {            return isroot;        }        bool getisoper() {            return isoper;        }        bool getisleft() {            return isleft;        }        char getoper() {            return oper;        }        int getnum() {            return num;        }        node* getfather() {            return father;        }        node* getleft() {            return left;        }        node* getright() {            return right;        }        int getno(){            return no;        }    private:        bool inbracket,isroot,isoper,isleft;        char oper;        int num,no;        node *father,*left,*right;};vector<node*> nodes;//节点的缓存,用于快速销毁树和节点编号 node::node(){    inbracket=isroot=isoper=isleft=false;    oper='+';    num=0;    no=nodes.size();    father=left=right=NULL;}string str;int pos;//输入的算式串 void end(){    system("pause");    exit(0);}void error(string str="") {    if(str!="")        cout<<str<<endl;    cout<<"HSSCalc exit."<<endl;    end();}bool cisnum(char c) {//判断此字符是否是数字     return c>='0'&&c<='9';}int getOperLevel(char c) {//判断此字符是否是允许字符 以及优先级排列     for(int i=0; i<row; i++) {        for(int j=0; j<col; j++) {            if(operTable[i][j]=='\0') {                break;            } else if(operTable[i][j]==c) {                return i;            }        }    }    return INVALID;}bool cisoper(char c){    //允许字符且不是 ( )    return getOperLevel(c)!=INVALID&&c!='('&&c!=')';}void check() {    int cnt=0;    if(str=="")//TODO 其他特殊输入情况         error("Empty string error.");    for(int i=0; i<(signed int)str.length(); i++) {        if(!cisnum(str[i])) {            if(getOperLevel(str[i])==INVALID) {//既不是数字 又不是允许字符                 error("Invalid char error.");            } else if(str[i]=='(') {//括号嵌套检查                 cnt++;            } else if(str[i]==')') {                cnt--;            }        }        if(cnt<0) {            error("Invalid bracket error.");        }    }    if(cnt!=0) {        error("Invalid bracket error.");    }}int getstage(node &n) {//判断此节点处于哪个阶段 来选择取到输入时需要如何处理     if(n.getleft()==NULL)        return STGLEFT;//没有左子节点     else if(!n.getisoper())        return STGOPER;//没有操作符     else if(n.getright()==NULL)        return STGRIGHT;//没有右子节点    else        return STGENDING;//用于检查后面是否有 ) 或需要优先级处理的操作符 }char nextchar() {    if(pos>=(signed int)str.length()-1)        return '\0';    else        return str[++pos];}int getStrNum() {//在算式串当前字符是数字字符时 得到这个完整数字     int num=0;    while(pos<(signed int)str.length()&&cisnum(str[pos])) {        num=num*10+str[pos]-'0';        pos++;    }    pos--;    return num;}int opcomp(char op1,char op2){//优先级比较 用于判断节点关系如何处理 //注意 需要op1在左 op2在右     int level1=getOperLevel(op1);    int level2=getOperLevel(op2);    if(level1==INVALID){        ostringstream oss;        oss<<"Compare error, pos "<<pos<<",oper "<<op1<<".";        error(oss.str());    }    if(level2==INVALID){        ostringstream oss;        oss<<"Compare error, pos "<<pos<<",oper "<<op2<<".";        error(oss.str());    }    if(level1<level2)        return LOWER;    else//同级运算符从左到右         return HIGHER;}void output(int num){    cout<<num<<endl;}node* newnode(){//动态分配 放入缓存     node *n=new node();    nodes.push_back(n);    return n;}void maketree(node &curr) {//根据输入和节点阶段 状态机建树     char inp;    node *addleft;    node *addright;    node *addcomp;    switch(getstage(curr)) {        case STGLEFT://没有左节点时             addleft=newnode();            inp=nextchar();            if(cisnum(inp)) {//是数字 则左节点是数字                 addleft->setnum(getStrNum());                curr.setleft(addleft);            } else if(inp=='(') {//左节点在括号中                 addleft->setinbrac(true);                curr.setleft(addleft);                maketree(*addleft);            } else {//error                ostringstream oss;                oss<<"236:Maketree error, pos "<<pos<<",Stage left.";                error(oss.str());            }        case STGOPER://没有操作符时             inp=nextchar();            if(cisoper(inp)){//是操作符 则赋值给自身                 curr.setoper(inp);            }else if(inp=='\0'){//字符串结束 因为括号嵌套完整 所以没有 ( ,算式串是单个数字                 if(!curr.getleft()->getisoper()){//其实不可能是操作符 因为会在stage left就报错                     output(curr.getleft()->getnum());                    end();                }else{//error                    ostringstream oss;                    oss<<"251:Maketree error, node "<<curr.getno()<<" pos "<<pos<<" stage oper,inp \\0.";                    error(oss.str());                }            }else{//error                ostringstream oss;                oss<<"256:Maketree error, node "<<curr.getno()<<" pos "<<pos<<" stage oper.";                error(oss.str());            }        case STGRIGHT://没有右节点时 类似stage left             addright=newnode();            inp=nextchar();            if(cisnum(inp)) {                addright->setnum(getStrNum());                curr.setright(addright);            } else if(inp=='(') {                addright->setinbrac(true);                curr.setright(addright);                maketree(*addright);            } else {                ostringstream oss;                oss<<"274:Maketree error, pos "<<pos<<",Stage right.";                error(oss.str());        }        default://STGENDING 用于检查 ) 和需要优先级处理的操作符             inp=nextchar();            if(cisoper(inp)){//如果是操作符                 addcomp=newnode();                addcomp->setoper(inp);                if(opcomp(curr.getoper(),inp)==HIGHER){//当前节点优先级比后面的高 是后面的的子节点                     if(!curr.getisroot())                        if(curr.getisleft())//将父关系转交                             curr.getfather()->setleft(addcomp);                        else                            curr.getfather()->setright(addcomp);                    addcomp->setinbrac(curr.getinbrac());//将当前节点作为子节点                     curr.setinbrac(false);                    addcomp->setisroot(curr.getisroot());                    curr.setisroot(false);                    addcomp->setleft(&curr);                }else{//优先级比它低 是它的父节点                     addcomp->setleft(curr.getright());//将右节点给它 将他作为右节点 如5+3*2                     curr.setright(addcomp);                }                maketree(*addcomp);//输入处理转移给它 让它接收右节点             }else if(inp!=')'&&inp!='\0'){//输入是 ) 或结束符时 会自动返回结束建树                 ostringstream oss;                oss<<"236:Maketree error, pos "<<pos<<",Stage ending.";                error(oss.str());            }    }}node* uptoroot(node *n){//从main函数的初始节点向上 拿到root节点 用于自上而下计算     while(!n->getisroot()){        n=n->getfather();    }    return n;}void deltree(){    while(nodes.size()!=0){        delete nodes.back();        nodes.pop_back();    }}int calc(node &n){    if(!n.getisoper()) return n.getnum();//如果是数字 则值就是本身     switch(n.getoper()){//根据操作符匹配计算方法         case '+':            return calc(*n.getleft())+calc(*n.getright());        break;        case '-':            return calc(*n.getleft())-calc(*n.getright());        break;        case '*':            return calc(*n.getleft())*calc(*n.getright());        break;        case '/':            return calc(*n.getleft())/calc(*n.getright());        break;    }//不能匹配则报错     ostringstream oss;    oss<<"355:Calc error, node "<<n.getno()<<" oper "<<n.getoper();    error(oss.str());    return INVALID;}int main() {    cout<<"Helix SStrict Calculator Beta"<<endl;    cout<<"Input:"<<endl;//输入算式串     cin>>str;    pos=-1;    check();//检查串     node *n=newnode();    n->setisroot(true);//建立根节点     maketree(*n);//建树     n=uptoroot(n);//上到root     output(calc(*n));//计算并输入     deltree();//销毁树 回收内存     cout<<"HSSCalc exit."<<endl;    system("pause");    return 0;}
0 0
原创粉丝点击