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;}
- Helix浅谈算式处理(C++计算器源码发布)
- c 语言简单计算器源码
- 计算器源码~控制台C语言
- 巧妙地用二叉树完成算式计算算法<计算器,二叉树,C++,独辟蹊径>
- 四则运算计算器 C 语言完美版源码
- C语言版 计算器-词法分析器-源码分析
- 达内课程-算式计算器实现
- 计算器输入计算式输出结果
- 浅谈Objective-C异常处理
- 计算器源码
- 计算器源码
- C语言写的仿WINDOWS 界面计算器源码
- C语言算式加减法运算
- 浅谈用C语言进行数字图像处理
- 浅谈C语言中断处理机制
- 计算器.c
- 【C#】计算器
- 计算器 abacus 1.0 发布
- Android 利用Sensor现实了传感器
- java 多线程 线程之间的通信
- 设计模式(6)-原型模式
- 20170226C++项目班05_C/C++错误异常处理
- ffmpeg移植android编译问题解决
- Helix浅谈算式处理(C++计算器源码发布)
- 被说了很多遍的设计模式---命令模式
- 基础练习 01字串
- hdu2841 Visible Trees(容斥原理)
- 欢迎使用CSDN-markdown编辑器
- 形态学在图像处理中的应用
- web常用小技术集合
- Tomcat 安全配置与性能优化
- python 内置map()和six.moves.map()区别