《模拟计算器程序-课程设计.doc》由会员分享,可在线阅读,更多相关《模拟计算器程序-课程设计.doc(14页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、【精品文档】如有侵权,请联系网站删除,仅供学习与交流模拟计算器程序-课程设计.精品文档.模拟计算器学生姓名:* 指导老师:*摘 要 本课程设计的课题是设计一个模拟计算器的程序,能够进行表达式的计算,并且表达式中可以包含Abs()和Sqrt()运算。在课程设计中,系统开发平台为Windows ,程序设计设计语言采用C+,程序运行平台为Windows 或 *nix。本程序的关键就是表达式的分离和处理,在程序设计中,采用了将输入的中缀表达式转化为后缀表达式的方法,具有可靠的运行效率。本程序做到了对输入的表达式(表达式可以包含浮点数并且Abs()和Sqrt()中可以嵌套子表达式)进行判定表达式是否合法
2、并且求出表达式的值的功能。经过一系列的调试运行,程序实现了设计目标,可以正确的处理用户输入的表达式,对海量级数据都能够通过计算机运算快速解决。关键词 C+程序设计;数据结构;表达式运算;栈;中缀表达式;后缀表达式;字符串处理;表达式合法判定;目 录1 引 言21.1课程设计目的31.2课程设计内容32 设计思路与方案33 详细实现43.1 表达式的合法判定43.2 中缀表达式转化为后缀表达式53.3 处理后缀表达式73.4 表达式嵌套处理84 运行环境与结果84.1 运行环境84.2 运行结果85 结束语11参考文献12附录1:模拟计算器源程序清单141 引 言本课程设计主要解决的是传统计算器
3、中,不能对表达式进行运算的问题,通过制作该计算器模拟程序,可以做到快速的求解表达式的值,并且能够判定用户输入的表达式是否合法。该模拟计算器的核心部分就在用户输入的中缀表达式的转化,程序中用到了“栈”的后进先出的基本性质。利用两个“栈”,一个“数据栈”,一个“运算符栈”来把中缀表达式转换成后缀表达式。最后利用后缀表达式来求解表达式的值。该算法的复杂度为O(n),能够高效、快速地求解表达式的值,提高用户的效率。1.1课程设计目的数据结构主要是研究计算机存储,组织数据,非数值计算程序设计问题中所出现的计算机操作对象以及它们之间的关系和操作的学科。数据结构是介于数学、计算机软件和计算机硬件之间的一门计
4、算机专业的核心课程,它是计算机程序设计、数据库、操作系统、编译原理及人工智能等的重要基础,广泛的应用于信息学、系统工程等各种领域。学习数据结构是为了将实际问题中涉及的对象在计算机中表示出来并对它们进行处理。通过课程设计可以提高学生的思维能力,促进学生的综合应用能力和专业素质的提高。模拟计算器程序主要利用了“栈”这种数据结构来把中缀表达式转化为后缀表达式,并且运用了递归的思想来解决Abs()和Sqrt()中嵌套表达式的问题,其中还有一些统计的思想来判定表达式是否合法的算法。1.2课程设计内容本次课程设计为计算器模拟程序,主要解决表达式计算的问题,实现分别按表达式处理的过程分解为几个子过程,详细的
5、求解过程如下:1 用户输入表达式。2 判定表达式是否合法。3 把中缀表达式转化为后缀表达式。4 求出后缀表达式的结果。5 输出表达式的结果。通过设计该程序,从而做到方便的求出一个表达式的值,而不需要一步一步进行运算。2 设计思路与方案本课程设计需要考虑许多的问题,首先是表达式的合法判断,然后是字符串表达式提取分离的问题,核心部分就是中缀表达式转化为后缀表达式。对于第一个问题,我是分步来判断,首先表达式中是否含有其它非法字符,然后判断括号是否合法,接着判断运算法两边是否合法,比如除法时,除数不能为零。对于第二个问题,我是直接转换的,从左到右遍历中缀表达式,把数据全部取出来。对于核心问题,利用了“
6、栈”这种“后进先出”的数据结构,利用两个“栈”,一个“数据栈”,一个“运算符栈”来把中缀表达式转换成后缀表达式。最后利用后缀表达式来求解表达式的值。 上面是数据处理的算法部分。本程序用户界面总共分为3个模块,分别是操作提示,数据输入,数据输出。如图2.1所示。图2.1 用户界面除了实现基本的功能外,我还增加了其它一些功能,比如支持输入数据为浮点数,更重要的是本程序还支持表达式的嵌套运算,例如:A(1+2*S(2)我的实现方法是利用函数的递归调用来解决此问题,即把1+2*S(2)看成一个子表达式,这个子表达式中2也看成子表达式。这样使得程序的适用范围更加的广泛,适应性更强,能支持更复杂的表达式的
7、运算。这也是本程序的优点之一。3 详细实现3.1 表达式的合法判定表达式的合法判定过程如图3.1所示是否存在其它字符括号是否合法运算符是否合法 图3.1 表达式的合法判定过程首先是其它字符的判定,从左到右遍历中缀表达式,看是否存在其它非法的。然后是判定括号是否的匹配是否和合法,首先把“(”对应为1,相应的“)”对应为-1。从左到右遍历表达式,如果遇到括号就加上其对应的值,用sum来保存其累加值。如果在中途出现sum小于零的情况,即出现“. )” 那么的情况,即非法。在遍历的最后,还要判断sum的值是否为零,如果为零就是合法,否则就是非法。代码如下: for(i=0;is.length();i+
8、) /检验括号是否合法,以及是否存在非法字符 if(!IsNum(si) & !IsSign(si) & si!=( & si!=) & si!=A & si!=S & si!=.)return false; if(si=()sum+=1; else if(si=)sum-=1; if(sum0)return false; /括号匹配不合法运算符判断是否合法,也是遍历一遍表达式,遇到“/”,看其后面的除数是否为零。这里要考虑表达式中出现负数的情况,因此特殊考虑“-”号,判断它的前面是“(”还是没有字符了,那么就是负数。3.2 中缀表达式转化为后缀表达式中缀表达式转化为后缀表达式,利用两个“栈”
9、,一个“数据栈”,一个“运算符栈”来把中缀表达式转换成后缀表达式。最后利用后缀表达式来求解表达式的值。设一个stack存后缀数据,一个rout栈存运算符。算法流程如下:(1)从右向左依次取得数据ch。(2)如果ch是操作数,直接加进stack中。(3)如果ch是运算符(含左右括号),则: a:如果ch = (,放入堆栈rout中。 b:如果ch = ),依次输出堆栈rout中的运算符,直到遇到(为止。 c:如果ch不是)或者(,那么就和堆栈rout顶点位置的运算符top做优先级比较。 1:如果ch优先级比rtop高,那么将ch放入堆栈rout。 2:如果ch优先级低于或者等于rtop,那么输出
10、top到stack中(直到!top或者满足 1),然后将ch放入堆栈rout。 可以看出算法复杂度是O(n)的,因此效率是比较高的,能够在1s内处理百万级别长度的表达式。算法的主要思想是利用“栈”的后进先出的特性,以及运算符的优先级,这里我们定义运算符的优先级;代码如下:int GetKey(char c) /定义运算符的关键字 int key; switch(c) case +:key=1;break; case -:key=1;break; case *:key=2;break; case /:key=2;break; case (:key=4;break; case ):key=5;br
11、eak; return key;中缀转化为后缀处理的核心代码如下: /* 双栈,sta1存放后缀表达式,sta2存放运算符符号*/ stackpair sta1,sta2; for(i=0;i=numi.second & sta2.top().second!=4) sta1.push(sta2.top(); sta2.pop(); sta2.push(numi); /放入当前运算符最后,后缀表达式就存放在sta1栈中,把sta1栈中的结果存放到SufExp中就得到了后缀表达式。3.3 处理后缀表达式这里也是运用“栈”来处理,运用栈模板在求值过程顺序扫描后缀表达式,每次遇到操作数便将它压入堆栈;
12、遇到运算符,则从栈中弹出两个操作数进行计算,然后再把结果压入堆栈,等到扫描结束时,则表达式的结果就求出来了。算法流程如图3.3所示: 后缀表达式 扫描并判定数据类型 从栈中取出数字进行运算 数字压入栈中 栈 结果压入栈中 输出最终结果图 3.3 处理后缀表达式流程核心代码如下:double Expression:GetAns() int i; double temp,num1,num2; /num1和num2为运算符两遍的操作数 stack sta; /数据栈 for(i=0;iSize;i+) if(!SufExpi.second) /为数据 sta.push(SufExpi.first);
13、 else /为运算符 num1=sta.top(); /取出第一个操作数 sta.pop(); num2=sta.top(); /取出第二个操作数 sta.pop(); temp=Cal(char)SufExpi.first,num2,num1); sta.push(temp); /放入操作数结果 Ans=sta.top(); return Ans; /返回最终结果3.4 表达式嵌套处理如果遇到A()和S()中含有表达式,而不是单纯的数字,例如A(1.1+3.4*S(2.5),那么就需要对其字表达式“1.1+3.4*S(2.5)”进行递归处理,这个子表达式中还含有子表达式“2.5”,然后再递
14、归处理,依次类推下去。其核心代码如下: if(si=A | si=S) /遇到Abs()或者Sqrt()递归处理子表达式 Expression temp; /创建子表达式 temp.Init(); for(j=0;i+j+2Posi+1;j+) /复制表达式 stj=si+j+2; stj=0; temp.s=st; /复制表达式 temp.SloveExp(); /得到子表达式的值 numk.first=(si=A?fabs(temp.Ans):sqrt(temp.Ans); numk.second=0; /标记为数据 if(si-1=- & (i-1=0 | si-2=()numk.fir
15、st=-numk.first; k+,i=Posi+1;4 运行环境与结果4.1 运行环境编译环境:Microsoft Visual C+ 6.0 / GNU GCC 4.8.1运行环境:Windows XP / Windows 7 / Linux Ubuntu13.044.2 运行结果表达式中含非法字符判断如图4.1所示:图4.1 非法字符判断表达式中非法括号匹配判断如图4.2所示:图4.2 非法括号匹配判断表达式中运算符判断如图4.3所示:图4.3 运算符判断表达式中有浮点数如图4.4所示:图4.4 表达式中有浮点数A()运算符中嵌套表达式如图4.5所示:图4.5 A()运算符中嵌套表达式
16、S()运算符中嵌套表达式如图4.6所示:图4.6 S()运算符中嵌套表达式复杂的表达式运算如图4.7所示:图4.7 复杂的表达式运算5 结束语通过两周的课程设计,我学会了如何写一个精简、快速、健壮的程序。一个好的程序应该是一个所占空间小、运行时间短、其他性能也好的程序。而要做出一个好的程序则应该通过对算法与其数据结构的时间复杂度和空间复杂度进行实现与改进。然而,实际上很难做到十全十美,原因是各要求有时相互抵触,要节约算法的执行时间往往要以牺牲更多的存储空间为代价:而为了节省存储空间又可能要以更多的时间为代价。因此,只能根据具体情况有所侧重:如果程序的使用次数较少,则应该力求算法简明易懂,而易于
17、转换为上机程序;如果程序反复多次使用,则应该尽可能选用快速算法;如果解决问题的数据量极大,机器的内存空间较小,则在编写算法时应该考虑如何节省空间。本次课程设计培养了了我们独立思考的能力,提高了我们的动手操作水平。在具体设计操作中,我们巩固了本学期所学的数据结构与算法的理论知识,进一步提高了自己的编程能力。这也是课程设计的最终目的所在。通过实际操作,开发了自己的逻辑思维能力,培养了分析问题、解决问题的能力。但在程序设计的过程中我也深刻的感受到自己实力的不足,无法灵活的运用各种工具和函数,对于课程所讲的东西也无法在脱离课本的情况中完成,我意识到自己在今后的学习生活中,一定要勤于思考,扎实掌握理论知
18、识,灵活运用课上所学的东西,做一个优秀的程序员。参考文献1Thomas H.Cormen, Charles E.Leiserson, Ronald L.Rivest. 北京. 算法导论(第三版)M. 机械工业出版社:Thomas H.Cormen, 20132 刘汝佳, 黄亮. 北京. 清华大学出版社. 算法艺术与信息学竞赛.3 王晓东. 北京. 清华大学出版社. 算法设计与分析(第二版).附录1:模拟计算器源程序清单/ 程序名称:Calculator.cpp/ 程序功能:模拟计算器/ 程序作者:*/ 学号: */ 最后修改日期:2013-7-5课题4:设计一个模拟计算器的程序,要求能对包含加
19、、减、乘、除、括号运算符及SQR和ABS函数的任意整型表达式进行求解。 要求:要检查有关运算的条件,并对错误的条件产生报警。我的代码在此基础上增加了几个功能:1. 不仅支持整数运算,还支持浮点数运算2. 可支持表达式的嵌套,Ex:A(1+2*S(3))#include #include #include #include #include #include #include using namespace std;#define mem(a,b) memset(a,b,sizeof(a)const int MaxLength=1010; /数组的最大存储空间bool IsNum(char c)
20、 /判断是否是数字 if(c=0 & c=9)return true; return false;bool IsSign(char c) /判断是否是运算符号 if(c=+ | c=- | c=* | c=/)return true; return false;int GetKey(char c) /定义运算符的关键字 int key; switch(c) case +:key=1;break; case -:key=1;break; case *:key=2;break; case /:key=2;break; case (:key=4;break; case ):key=5;break;
21、return key;double Cal(char c,double a,double b) /根据运算符来计算运算结果 switch(c) case +:return a+b; case -:return a-b; case *:return a*b; case /:return a/b; return 0;class Graph /图形界面类public: void Window(); /图形窗口函数void Graph:Window() cout |=欢迎使用本计算器=|endl; cout | 1.本计算器支持表达式计算,输入数据为实数 |endl; cout | 2.可支持表达式的
22、嵌套,Ex:A(1+2*S(3) |endl; cout |-|endl; cout | 7 8 9 + / |endl; cout | 4 5 6 - A(abs) |endl; cout | 1 2 3 * S(sqrt) |endl; cout |-|endl;class Expressionpublic: void Input(); /表达式输入 void Init(); /表达式数据初始化 bool SloveExp(); /表达式处理过程 bool CheckExp(); /检查表达式是否合法 void GetSuffix(); /得到后缀表达式 double GetAns();
23、/根据后缀表达式来得到结果 void Display(); /输出表达式结果private: int Size; /后缀表达式的长度 string s; /表达式存储 bool HaveAns; /表达式是否有结果 double Ans; /表达式结果 pair SufExpMaxLength; /后缀表达式存储 int PosMaxLength; /中缀表达式中(对应的)数组下标位置void Expression:Input() /表达式输入 couts;void Expression:Init() /表达式数据初始化 HaveAns=false; mem(Pos,-1);bool Expr
24、ession:SloveExp() if(HaveAns=CheckExp()=0)return HaveAns=false; /表达式不合法,退出 GetSuffix(); /得到后缀表达式 GetAns(); /根据后缀表达式来得到结果 return true;bool Expression:CheckExp() /检查表达式是否合法 int i,j,cnt; int sum=0; for(i=0;is.length();i+) /检验括号是否合法,以及是否存在非法字符 if(!IsNum(si) & !IsSign(si) & si!=( & si!=) & si!=A & si!=S
25、& si!=.)return false; if(si=()sum+=1; else if(si=)sum-=1; if(sum0)return false; /括号匹配不合法 if(sum!=0)return false; for(i=0;is.length();i+) if(IsSign(si) if(si=/ & si+1=0) /判断除法的被除数是不是为零 return false; for(i=0;i=0;j-) /遇到)括号 if(sj=)sum+=1; else if(sj=()sum-=1; if(sum=0) /如果sum的值为0,那么找到)的匹配括号 Posj=i; bre
26、ak; return true; /表达式正确void Expression:GetSuffix() /得到后缀表达式 int i,j,w,k=0; char stMaxLength; pair numMaxLength; /保存后缀表达式,double为数据,int标记符号 for(i=0;is.length();i+) if(si=A | si=S) /遇到Abs()或者Sqrt()递归处理子表达式 Expression temp; /创建子表达式 temp.Init(); for(j=0;i+j+2Posi+1;j+) /复制表达式 stj=si+j+2; stj=0; temp.s=s
27、t; /复制表达式 temp.SloveExp(); /得到子表达式的值 numk.first=(si=A?fabs(temp.Ans):sqrt(temp.Ans); numk.second=0; /标记为数据 if(si-1=- & (i-1=0 | si-2=()numk.first=-numk.first; k+; i=Posi+1; else if(IsNum(si) /处理数据 double sum=0; int ok=0,w=1; /*把数据提取出来*/ for(j=i;js.length() & (IsNum(sj) | sj=.);j+) if(sj!=.)sum=sum*1
28、0+(double)(sj-0); else ok=1,w=0; if(ok)w+; /小数点位数统计 numk.first=sum/pow(10.0,(double)(w-1); /处理浮点数 numk.second=0; if(si-1=- & (i-1=0 | si-2=()numk.first=-numk.first; k+; i=j-1; else /为符号,直接存入,特殊考虑负数 if(si=- & (i=0 | si-1=()continue; numk.first=(double)si; numk+.second=GetKey(si); /* 双栈,sta1存放后缀表达式,st
29、a2存放运算符符号*/ stackpair sta1,sta2; for(i=0;i=numi.second & sta2.top().second!=4) sta1.push(sta2.top(); sta2.pop(); sta2.push(numi); /放入当前运算符 while(!sta2.empty() /如果栈sta2非空,则继续取出sta2中的数据到sta中 sta1.push(sta2.top(); sta2.pop(); Size=sta1.size(); /后缀表达式长度 for(i=Size-1;!sta1.empty();i-) /后缀表达式传递给SufExp数组 S
30、ufExpi=sta1.top(); sta1.pop();double Expression:GetAns() int i; double temp,num1,num2; /num1和num2为运算符两遍的操作数 stack sta; /数据栈 for(i=0;iSize;i+) if(!SufExpi.second) /为数据 sta.push(SufExpi.first); else /为运算符 num1=sta.top(); /取出第一个操作数 sta.pop(); num2=sta.top(); /取出第二个操作数 sta.pop(); temp=Cal(char)SufExpi.f
31、irst,num2,num1); sta.push(temp); /放入操作数结果 Ans=sta.top(); return Ans; /返回最终结果void Expression:Display() /结果数据函数 if(HaveAns) cout表达式的结果是: ; coutAnsendl; else cout对不起,您输入的表达式不符合规范!endl;int main()/ freopen(in.txt,r,stdin); string IsContinue; Graph G; Expression E; do system(cls); G.Window(); /初始化界面 E.Init(); /表达式相关数据初始化 E.Input(); /表达式输入 E.SloveExp(); /表达式运算 E.Display(); /表达式结果输出 coutIsContinue; while(IsContinue0=y | IsContinue0=y); cout欢迎使用endl; return 0;
限制150内