【C++讨论站】可以完成中缀表达式转后缀表达式的简单计算器
作者:互联网
1.问题描述
对于给定的一个表达式,表达式中可以包括常数、算术运行符(包括:“+”、 “-”、“*”、“/”、“%”(求余运算)、“^”(乘幂运算)、“!”(阶乘运算))和括 号,编写一个简单计算器,实现表达式的计算。
基本要求:从键盘输入一个正确的表达式,将表达式转换为对应的后缀表达式,并计算后缀 表达式的值。对于表达式中的简单错误(如,除数不能为零、负数无法求阶乘等),能够给出提 示,并给出错误信息;表达式中可以包括单个字母表示的变量。
提高要求:完成基本要求的基础上,能够支持一部分不规范的表达式(如,3-3+-6),支持 科学计数法表示的数(如,19971400000000 表示为:1.99714E13),并具有图形用户界面的简单计算器
2.需求分析
软件的基本功能:由键盘输入中缀表达式,程序可以将输入的中缀表达式转换成对应的后缀表达式,并计算后缀表达式的值。对于在输入时发生的简单错误,程序可以给出提示。本程序支持含负数、小数、多位数等多种操作数的处理,可以计算含加、减、乘、除、求余、求幂等多种运算符的表达式,并能判断表达式括号是否匹配。
输入/输出形式:用户可以通过控制台,根据输入提示。
输入形式:
①正确的不含字母变量的中缀表达式;
②含有简单错误的中缀表达式。
输出形式:
①对于正确的中缀表达式,可以输出其转化后的后缀表达式及表达式的计算结果;
②对于含有简单错误的中缀表达式,程序将自动输出错误提示,并给出错误信息。
测试数据要求:用户可以输入一个符合要求的中缀表达式,也可以输入一个包含简单错误的表达式。表达式中可以包括各种类型的常数以及负数等,操作符包括(+、-、*、/、%、^)等,同时表达式还可以包括各种括号。
- 概要设计
(1)抽象数据类型:
根据题目的要求,考虑用栈类型比较适合。
本程序总共设计了三个栈C1、C2、C3。C1用于存储输入的中缀表达式,C2为栈用于转化过程中对数和操作符进行压栈,C3用于元素出栈后进行相关运算,并将最终结果进行重新压栈。在栈操作的过程中用top变量,对栈内的存储情况进行标记。如果top=0则表示栈为空。
(2)主程序流程:
图1:主程序流程图
(3)模块调用关系:
本程序中函数包括:main函 数,adorn函数,fun函数,PrintList函数,input函数,Change函数,systemmenu函数,easycalculate函数,easymenu函数
- Main函数
通过switch-case函数对系统界面进行初步的实现,用户可以通过输入1、2或其他数字选择进入系统的不同功能。同时使用system函数对命令指示行进行DOS操作,实现在实现新功能时清除缓存区。
- adorn函数
此函数为装饰函数,通过ASCLL码判断如果是数字则将ASCLL码-48转换为字符,强制在字符串的结尾加上\n,方便计算字符串内的个数 。进而解决了系统无法区分数字与运算符的问题
- Fun函数
此函数的作用为判断括号是否匹配 (1匹配,0不匹配)。设置了两个字符变量L\R
- PrintList函数
将表达式最后计算的结果输出,如果为结果大于1000或小于0.001,则以科学计数法输出
- Input函数
对用户从键盘输入的数据进行读取,保存在字符类型的数组中,以便后续使用
- Change函数
- 此函数功能为将中缀表达式转换为后缀表达式并进行计算 ,函数首先会调 用Fun函数已检测表达式括号是否匹配,若不匹配则返回主函数。从左至右扫描 中缀表达式,遇到运算符时,比较其与s1栈顶运算符的优先级遇到操作数时, 将其压入s2遇到括号时如果是左括号’(’,则直接压入s1如果是右括号’)’, 则依次弹出s1栈顶的运算符,并压入s2中,直到遇到左括号’('为止,将这个 左括号从s1中弹出丢弃(这时候消除了一对括号)将s1中剩余的运算符依次弹 出并压入s2中依次弹出s2中的元素并输出,并将输出结果逆序,即得到后缀表 达式。从左至右扫描,若为数字,依次压入栈遇到运算符,依次弹出s1,s2, 计算s2(+,-,*,/)s1的结果并压入栈。
- systemmenu函数
本函数用于打印当用户进入系统时的操作界面,以便用户对系统可实现的操作功能进行选择。
- easycalculate函数
对简单的二元计算式进行‘+’‘-’‘*’‘/’的运算
- easymenu函数
- 此函数是简单计算器的系统界面,在该函数使用中通过调用easycalculate函数,对简单的二元运算式进行运算
函数调用关系如下:
图2:函数调用关系
4.详细设计
(1)实现概要设计的数据类型:
根据题目的要求,考虑用栈类型比较适合。
本程序总共设计了三个栈C1、C2、C3。C1用于存储输入的中缀表达式,C2为栈用于转化过程中对数和操作符进行压栈,C3用于元素出栈后进行相关运算,并将最终结果进行重新压栈。在栈操作的过程中用top变量,对栈内的存储情况进行标记。如果top=0则表示栈为空。
(2)主程序以及其它模块的算法描述:
主函数具体代码:
int main()
{
while(1)
{
system("cls");
systemmenu();
int n;
cin>>n;
switch(n)
{
case 1: system("cls");
while(1)
{
cout<<"欢迎来到简单计算器功能"<<endl;
cout<<"本功能支持'+''-''*''/'四种运算符号";
cout<<"[且运算符号需为英文字符]"<<endl;
easymenu();
int flag;
cout<<"*************************"<<endl;
cout<<"是否继续简单计算器功能?"<<endl;
cout<<"1-YES"<<endl;
cout<<"2-NO"<<endl;
cout<<"*************************"<<endl;
cin>>flag;
if(flag==2)
{
break;
}
}
break;
case 2: system("cls");//实现启动器的DOS功能,清除显示器屏幕上的内容,使DOS提示符到屏幕左上角
while(1)
{
fflush(stdin);//清除缓冲区的值
int count=0;
int i;
int j;
int m;
int n;
int top=0;
int v=-1;
int u=0;
adorn('-',90);
cout<<"**请输入需要计算的中缀表达式,直接回车即可,注意本系统只识别英文符号**:" <<endl;
cout<<"如果想退出本界面,请输入‘#’号键"<<endl;
input();
if(*c1=='#')
{
break;
}
else
{
Change(c1);
}
}
default:system("cls");
cout<<"**************"<<endl;
cout<<"感谢您的使用!" <<endl;
cout<<"我们下次再会!"<<endl;
cout<<"**************"<<endl;
goto over;
}
}
over:
return 0;
}
这个函数主要调用了实现功能的各个函数,并且对本程序实现的功能进行选择。若用户输入“1”则进入简单计算器的界面,若用户输入“2”进入中缀表达式转后缀表达式的界面,若输入其他数字则退出该程序。在进入每个界面后程序会调用system("cls");函数对DOS进行操作,清空命令指示行中的缓存信息。
图3:主函数关系流程
(3)其它模块的算法描述
表达式转换函数,并计算后缀表达式的值
功能:将中缀表达式转换为后缀表达式,其中infix是输入的中缀表达式,
Char Change(char c1[])
{
fflush(stdin);
input();
if(*c1=='#')
{
main();//返回主函数
}
else
{
Change(c1);
}
PrintList(top,a);
}
首先检验用户输入的中缀表达式进行括号匹配检验,若用户输入的中缀表达式中的括号不匹配的话,则返回主程序。)如果遇到操作数,我们就直接将其存放在C3数组中。如果遇到操作符,则我们将其放入到栈中,遇到左括号时我们也将其放入栈C2中。如果遇到一个右括号,则将栈元素弹出,将弹出的操作符输出直到遇到左括号为止。如果遇到任何其他的操作符,如(“+”, “*”,“(”)等,从栈中弹出元素直到遇到发现更低优先级的元素(或者栈为空)为止。弹出完这些元素后,才将遇到的操作符压入到栈中。有一点需要注意,只有在遇到" ) "的情况下我们才弹出" ( ",其他情况我们都不会弹出" ( "。如果我们读到了输入的末尾,则将栈中所有元素依次弹出。
最后对转换好的后缀表达式进行计算。从左向右进行扫描,遇到操作数入栈,遇到运算符就弹出两个操作数,计算后入栈,重复操作至扫描结束,计算的值为结果。
4:中缀表达式转后缀表达式流程
图5:后缀表达式计算流程
查看用户输入的表达式中的括号是否匹配
Int fun(char *c1,int s,int e)
{
if(c1[s] == L)
{
int x = s+1;//x为s的下一个字符
while((x<=e)) // 搜索匹配的右括号
{
if(p == q) // 再对已匹配括号里面的括号进行匹配
{
if(fun(c1, s+1, x-1) == 0) // 递归调用,从最外层的括号分别向内匹配
return 0;
}
}
if(p>q)
return 0//如果不匹配返回0;
}
}
return 1;//都匹配上返回1
}
设立两个标志点L、R用来记录左右输入的括号符号是否对称,当检测到左括号时存入字符串C1中。若识别到右括号则直接跳出循环。当用户输入的表达式遍历结束以后。依次从每一个待匹配括号后寻找匹配的右括号。再对已匹配括号里面的括号进行匹配,递归调用,从最外层的括号分别向内匹配。如果括号匹配返回1,如果括号不匹配返回0.
图6:括号匹配函数流程
计算器功能界面:
第一次输入用户需输入完整的二元计算式。当用户选择继续使用本功能时,显示第一位数字,用户只需要对运算符与数字进行输入即可 。程序会对运算式进行再次运算 。
图7:二元简单计算器流程
5.不同方案的分析说明
在程序对用户从键盘输入的键盘上获取的数据如何去识别是运算符还是数字上。通过ASCLL码判断如果是数字则将ASCLL码-48转换为字符类型的数组中进行存储,最后强制在字符串的结尾加上\n,方便计算字符串内的个数。在小组探讨的过程之中,诞生了两个计划方案,一个是利用字符串进行组合,一个是利ASCLL码进行变换。本人之所以采用第二种方法的原因为,采用此方法在用户输入的时候便可以直接对用户输入的信息是否正确进行检验。程序不用为对异常检验再次对表达式进行转换。从而减少了计算机时间和空间上的浪费。同时程序创建了三个栈,一个用来储存中缀表达式、一个用来为栈用于转化过程中对数和操作符进行压栈、一个用来元素出栈后进行相关运算,并将最终结果进行重新压栈。使程序在进行各数据的调用的时候更加方便。同时在识别用户输入的中缀表达式的过程中,通过对函数的递归调用,在牺牲一定的空间时提高了时间运行效率,从而对表达式从外到内的括号是否匹配进行检查。
6.创新性说明
使用了栈的数据结构,因为本题代码是通过数组进行构建所以为顺序栈。在创建栈的时候使用了字符串数组直接创建的方法,达到了在需要的时候可以取出非栈顶元素的效果。在代码异常处理方面,程序针对一些计算错误进行了异常的判别与处理。比如除数不能为0、取余不能为零等等。程序在运行的过程中如果遇到了错误异常,系统会对错误类型进行提示,并返回上一步的操作步骤。在本程序设计过程之中,通过DOS的一些命令,对命令指示符进行了部分的操作,完成了简单的界面设计与菜单设计,从而增强了用户在操作系统时的舒适性。同时在源代码的书写过程之中,分为了栈作用、函数定义、函数实现、主函数部分;并在函数书写的过程之中加入注释,增强了代码的可读性。同时在函数的命名时,采用了利用函数作用的英文释义进行命名,为后续代码的更新与完善提供了便利。同时函数对每种运算的函数实现进行了单独封装,保证了代码整体的安全性的同时也为他人为本程序代码进行阅读时提供了便利,方便他人优化本代码。
7.编码与调试分析
1)目前编码与调试过程中遇到的问题及解决办法:
【问题】无法处理多位数和小数。
解决办法:通过ASCLL码判断如果是数字则将ASCLL码-48转换为字符类型的数组中进行存储,最后强制在字符串的结尾加上\n,方便计算字符串内的个数
解决此问题的核心代码:
for(int i=0;i<n;i++)
{
putchar(adorn);
}
putchar('\n');
}
8、使用说明
进入菜单,根据提示进行选择:
- .若要使用简单计算器,选择‘1’;
- .若要输入中缀表达式并求值,选择‘2’;
(c).若要退出程序,选择其他数字;
(d).若输入出现错误,则返回主菜单,重新输入。
9.团队合作与心得体会
(1)讨论的问题、结果和团队成员
针对程序无法区分数字和运算符问题,进行了小组讨论,也得到了陈锐一同学的帮助,清楚地理解了将中缀表达式转换为后缀表达式的算法思想。通过我与陈锐一同学的共同探讨过ASCLL码判断如果是数字则将ASCLL码-48转换为字符;强制在字符串的结尾加上\n,方便计算字符串内的个数。
(2)自主学习和心得体会
在课程设计过程中,特别是在代码编写和调试的过程中,自学了很多新的知识。
- 清屏函数:
它也是包含于<stdlib.h>头文件中的,使用形式为system("CLS")。主要功能就是清空屏幕。主要是对DOS的操作,除了cls system函数还有如下功能
dir | 显示指定路径上所有文件或目录的信息 |
md | 建立目录 |
rd | 删除目录 |
cd | 进入指定目录 |
copy | 拷贝文件 |
del | 删除文件 |
ren | 改名 |
type | 显示文本文件 |
discopy | 磁盘复制 |
deltree | 删除目录树 |
mem | 查看你的计算机内存有多少,以及内存的使用情况 |
chkdsk | 检查你的磁盘的使用情况。 |
sys | 传递系统文件命令 |
pass | 设定DOS寻找.COM、.EXE、.BAT文件的所在目录 |
cls | 清除显示器屏幕上的内容,使DOS提示符到屏幕左上角 |
Time | 显示和设置DOS的系统时间 |
date | 显示和设置DOS的系统日期 |
ver | 显示正在运行的DOS系统版本号 |
表1:有关于DOS操作的相关命令
- Getchar:
getchar()是C语言中的函数,C++中也包含了该函数。getchar()函数的作用是从标准的输入stdin中读取字符。也就是说,getchar()函数以字符为单位对输入的数据进行读取。在控制台中通过键盘输入数据时,以回车键作为结束标志。当输入结束后,键盘输入的数据连同回车键一起被输入到输入缓冲区中。在程序中第一次调用getchar()函数从输入缓冲区中读取一个字节的数据。需要注意的是,如果此时在程序中第二次调用getchar()函数,因为此时输入缓冲区中还有回车键的数据没有被读出,第二个getchar()函数读出的是回车符。
3)math.h 数学函数库,一些数学计算的公式的具体实现是放在math.h里,具体有:
1 三角函数
double sin (double x); x的正弦值
double cos (double x); x的余弦值
double tan (double x); x的正切值
2 反三角函数
double asin (double x); 结果介于[-PI/2, PI/2],x值域为[-1,1]
double acos (double x); 结果介于[0, PI],x值域为[-1,1]
double atan (double x); 反正切(主值), 结果介于[-PI/2, PI/2]
double atan2 (double y, double x); 反正切(整圆值), 结果介于[-PI, PI]
3 双曲三角函数
double sinh (double x); x的双曲正弦值
double cosh (double x); x的双曲余弦值
double tanh (double x); x的双曲正切值
4 指数与对数
double exp (double x); 幂函数e^x
double pow (double x, double y); x^y,如果x=0且y<=0,或者x<0且y不是整型数,将产生定义域错误
double sqrt (double x); x的平方根,其中x>=0
double log (double x); 以e为底的对数,自然对数,x>0
double log10 (double x); 以10为底的对数,x>0
5 取整
double ceil (double x); 取上整
double floor (double x); 取下整
6 绝对值
do
1.问题描述
对于给定的一个表达式,表达式中可以包括常数、算术运行符(包括:“+”、 “-”、“*”、“/”、“%”(求余运算)、“^”(乘幂运算)、“!”(阶乘运算))和括 号,编写一个简单计算器,实现表达式的计算。
基本要求:从键盘输入一个正确的表达式,将表达式转换为对应的后缀表达式,并计算后缀 表达式的值。对于表达式中的简单错误(如,除数不能为零、负数无法求阶乘等),能够给出提 示,并给出错误信息;表达式中可以包括单个字母表示的变量。
提高要求:完成基本要求的基础上,能够支持一部分不规范的表达式(如,3-3+-6),支持 科学计数法表示的数(如,19971400000000 表示为:1.99714E13),并具有图形用户界面的简单计算器
2.需求分析
软件的基本功能:由键盘输入中缀表达式,程序可以将输入的中缀表达式转换成对应的后缀表达式,并计算后缀表达式的值。对于在输入时发生的简单错误,程序可以给出提示。本程序支持含负数、小数、多位数等多种操作数的处理,可以计算含加、减、乘、除、求余、求幂等多种运算符的表达式,并能判断表达式括号是否匹配。
输入/输出形式:用户可以通过控制台,根据输入提示。
输入形式:
①正确的不含字母变量的中缀表达式;
②含有简单错误的中缀表达式。
输出形式:
①对于正确的中缀表达式,可以输出其转化后的后缀表达式及表达式的计算结果;
②对于含有简单错误的中缀表达式,程序将自动输出错误提示,并给出错误信息。
测试数据要求:用户可以输入一个符合要求的中缀表达式,也可以输入一个包含简单错误的表达式。表达式中可以包括各种类型的常数以及负数等,操作符包括(+、-、*、/、%、^)等,同时表达式还可以包括各种括号。
- 概要设计
(1)抽象数据类型:
根据题目的要求,考虑用栈类型比较适合。
本程序总共设计了三个栈C1、C2、C3。C1用于存储输入的中缀表达式,C2为栈用于转化过程中对数和操作符进行压栈,C3用于元素出栈后进行相关运算,并将最终结果进行重新压栈。在栈操作的过程中用top变量,对栈内的存储情况进行标记。如果top=0则表示栈为空。
(2)主程序流程:
图1:主程序流程图
(3)模块调用关系:
本程序中函数包括:main函 数,adorn函数,fun函数,PrintList函数,input函数,Change函数,systemmenu函数,easycalculate函数,easymenu函数
- Main函数
通过switch-case函数对系统界面进行初步的实现,用户可以通过输入1、2或其他数字选择进入系统的不同功能。同时使用system函数对命令指示行进行DOS操作,实现在实现新功能时清除缓存区。
- adorn函数
此函数为装饰函数,通过ASCLL码判断如果是数字则将ASCLL码-48转换为字符,强制在字符串的结尾加上\n,方便计算字符串内的个数 。进而解决了系统无法区分数字与运算符的问题
- Fun函数
此函数的作用为判断括号是否匹配 (1匹配,0不匹配)。设置了两个字符变量L\R
- PrintList函数
将表达式最后计算的结果输出,如果为结果大于1000或小于0.001,则以科学计数法输出
- Input函数
对用户从键盘输入的数据进行读取,保存在字符类型的数组中,以便后续使用
- Change函数
- 此函数功能为将中缀表达式转换为后缀表达式并进行计算 ,函数首先会调 用Fun函数已检测表达式括号是否匹配,若不匹配则返回主函数。从左至右扫描 中缀表达式,遇到运算符时,比较其与s1栈顶运算符的优先级遇到操作数时, 将其压入s2遇到括号时如果是左括号’(’,则直接压入s1如果是右括号’)’, 则依次弹出s1栈顶的运算符,并压入s2中,直到遇到左括号’('为止,将这个 左括号从s1中弹出丢弃(这时候消除了一对括号)将s1中剩余的运算符依次弹 出并压入s2中依次弹出s2中的元素并输出,并将输出结果逆序,即得到后缀表 达式。从左至右扫描,若为数字,依次压入栈遇到运算符,依次弹出s1,s2, 计算s2(+,-,*,/)s1的结果并压入栈。
- systemmenu函数
本函数用于打印当用户进入系统时的操作界面,以便用户对系统可实现的操作功能进行选择。
- easycalculate函数
对简单的二元计算式进行‘+’‘-’‘*’‘/’的运算
- easymenu函数
- 此函数是简单计算器的系统界面,在该函数使用中通过调用easycalculate函数,对简单的二元运算式进行运算
函数调用关系如下:
图2:函数调用关系
4.详细设计
(1)实现概要设计的数据类型:
根据题目的要求,考虑用栈类型比较适合。
本程序总共设计了三个栈C1、C2、C3。C1用于存储输入的中缀表达式,C2为栈用于转化过程中对数和操作符进行压栈,C3用于元素出栈后进行相关运算,并将最终结果进行重新压栈。在栈操作的过程中用top变量,对栈内的存储情况进行标记。如果top=0则表示栈为空。
(2)主程序以及其它模块的算法描述:
主函数具体代码:
int main()
{
while(1)
{
system("cls");
systemmenu();
int n;
cin>>n;
switch(n)
{
case 1: system("cls");
while(1)
{
cout<<"欢迎来到简单计算器功能"<<endl;
cout<<"本功能支持'+''-''*''/'四种运算符号";
cout<<"[且运算符号需为英文字符]"<<endl;
easymenu();
int flag;
cout<<"*************************"<<endl;
cout<<"是否继续简单计算器功能?"<<endl;
cout<<"1-YES"<<endl;
cout<<"2-NO"<<endl;
cout<<"*************************"<<endl;
cin>>flag;
if(flag==2)
{
break;
}
}
break;
case 2: system("cls");//实现启动器的DOS功能,清除显示器屏幕上的内容,使DOS提示符到屏幕左上角
while(1)
{
fflush(stdin);//清除缓冲区的值
int count=0;
int i;
int j;
int m;
int n;
int top=0;
int v=-1;
int u=0;
adorn('-',90);
cout<<"**请输入需要计算的中缀表达式,直接回车即可,注意本系统只识别英文符号**:" <<endl;
cout<<"如果想退出本界面,请输入‘#’号键"<<endl;
input();
if(*c1=='#')
{
break;
}
else
{
Change(c1);
}
}
default:system("cls");
cout<<"**************"<<endl;
cout<<"感谢您的使用!" <<endl;
cout<<"我们下次再会!"<<endl;
cout<<"**************"<<endl;
goto over;
}
}
over:
return 0;
}
这个函数主要调用了实现功能的各个函数,并且对本程序实现的功能进行选择。若用户输入“1”则进入简单计算器的界面,若用户输入“2”进入中缀表达式转后缀表达式的界面,若输入其他数字则退出该程序。在进入每个界面后程序会调用system("cls");函数对DOS进行操作,清空命令指示行中的缓存信息。
图3:主函数关系流程
(3)其它模块的算法描述
表达式转换函数,并计算后缀表达式的值
功能:将中缀表达式转换为后缀表达式,其中infix是输入的中缀表达式,
Char Change(char c1[])
{
fflush(stdin);
input();
if(*c1=='#')
{
main();//返回主函数
}
else
{
Change(c1);
}
PrintList(top,a);
}
首先检验用户输入的中缀表达式进行括号匹配检验,若用户输入的中缀表达式中的括号不匹配的话,则返回主程序。)如果遇到操作数,我们就直接将其存放在C3数组中。如果遇到操作符,则我们将其放入到栈中,遇到左括号时我们也将其放入栈C2中。如果遇到一个右括号,则将栈元素弹出,将弹出的操作符输出直到遇到左括号为止。如果遇到任何其他的操作符,如(“+”, “*”,“(”)等,从栈中弹出元素直到遇到发现更低优先级的元素(或者栈为空)为止。弹出完这些元素后,才将遇到的操作符压入到栈中。有一点需要注意,只有在遇到" ) "的情况下我们才弹出" ( ",其他情况我们都不会弹出" ( "。如果我们读到了输入的末尾,则将栈中所有元素依次弹出。
最后对转换好的后缀表达式进行计算。从左向右进行扫描,遇到操作数入栈,遇到运算符就弹出两个操作数,计算后入栈,重复操作至扫描结束,计算的值为结果。
查看用户输入的表达式中的括号是否匹配
Int fun(char *c1,int s,int e)
{
if(c1[s] == L)
{
int x = s+1;//x为s的下一个字符
while((x<=e)) // 搜索匹配的右括号
{
if(p == q) // 再对已匹配括号里面的括号进行匹配
{
if(fun(c1, s+1, x-1) == 0) // 递归调用,从最外层的括号分别向内匹配
return 0;
}
}
if(p>q)
return 0//如果不匹配返回0;
}
}
return 1;//都匹配上返回1
}
设立两个标志点L、R用来记录左右输入的括号符号是否对称,当检测到左括号时存入字符串C1中。若识别到右括号则直接跳出循环。当用户输入的表达式遍历结束以后。依次从每一个待匹配括号后寻找匹配的右括号。再对已匹配括号里面的括号进行匹配,递归调用,从最外层的括号分别向内匹配。如果括号匹配返回1,如果括号不匹配返回0.
计算器功能界面:
第一次输入用户需输入完整的二元计算式。当用户选择继续使用本功能时,显示第一位数字,用户只需要对运算符与数字进行输入即可 。程序会对运算式进行再次运算 。
5.不同方案的分析说明
在程序对用户从键盘输入的键盘上获取的数据如何去识别是运算符还是数字上。通过ASCLL码判断如果是数字则将ASCLL码-48转换为字符类型的数组中进行存储,最后强制在字符串的结尾加上\n,方便计算字符串内的个数。在小组探讨的过程之中,诞生了两个计划方案,一个是利用字符串进行组合,一个是利ASCLL码进行变换。本人之所以采用第二种方法的原因为,采用此方法在用户输入的时候便可以直接对用户输入的信息是否正确进行检验。程序不用为对异常检验再次对表达式进行转换。从而减少了计算机时间和空间上的浪费。同时程序创建了三个栈,一个用来储存中缀表达式、一个用来为栈用于转化过程中对数和操作符进行压栈、一个用来元素出栈后进行相关运算,并将最终结果进行重新压栈。使程序在进行各数据的调用的时候更加方便。同时在识别用户输入的中缀表达式的过程中,通过对函数的递归调用,在牺牲一定的空间时提高了时间运行效率,从而对表达式从外到内的括号是否匹配进行检查。
6.创新性说明
使用了栈的数据结构,因为本题代码是通过数组进行构建所以为顺序栈。在创建栈的时候使用了字符串数组直接创建的方法,达到了在需要的时候可以取出非栈顶元素的效果。在代码异常处理方面,程序针对一些计算错误进行了异常的判别与处理。比如除数不能为0、取余不能为零等等。程序在运行的过程中如果遇到了错误异常,系统会对错误类型进行提示,并返回上一步的操作步骤。在本程序设计过程之中,通过DOS的一些命令,对命令指示符进行了部分的操作,完成了简单的界面设计与菜单设计,从而增强了用户在操作系统时的舒适性。同时在源代码的书写过程之中,分为了栈作用、函数定义、函数实现、主函数部分;并在函数书写的过程之中加入注释,增强了代码的可读性。同时在函数的命名时,采用了利用函数作用的英文释义进行命名,为后续代码的更新与完善提供了便利。同时函数对每种运算的函数实现进行了单独封装,保证了代码整体的安全性的同时也为他人为本程序代码进行阅读时提供了便利,方便他人优化本代码。
7.编码与调试分析
1)目前编码与调试过程中遇到的问题及解决办法:
【问题】无法处理多位数和小数。
解决办法:通过ASCLL码判断如果是数字则将ASCLL码-48转换为字符类型的数组中进行存储,最后强制在字符串的结尾加上\n,方便计算字符串内的个数
解决此问题的核心代码:
for(int i=0;i<n;i++)
{
putchar(adorn);
}
putchar('\n');
}
8、使用说明
进入菜单,根据提示进行选择:
- .若要使用简单计算器,选择‘1’;
- .若要输入中缀表达式并求值,选择‘2’;
(c).若要退出程序,选择其他数字;
(d).若输入出现错误,则返回主菜单,重新输入。
9.测试用例和测试结果
(1)含小数、多位数、负数及括号的表达式显示结果:
(2)含求余、求幂的表达式的显示结果:
(3)除法、求余时对于除数为零的错误提示:
得体会
(1)讨论的问题、结果和团队成员
针对程序无法区分数字和运算符问题,进行了小组讨论,也得到了陈锐一同学的帮助,清楚地理解了将中缀表达式转换为后缀表达式的算法思想。通过我与陈锐一同学的共同探讨过ASCLL码判断如果是数字则将ASCLL码-48转换为字符;强制在字符串的结尾加上\n,方便计算字符串内的个数。
(2)自主学习和心得体会
在课程设计过程中,特别是在代码编写和调试的过程中,自学了很多新的知识。
- 清屏函数:
它也是包含于<stdlib.h>头文件中的,使用形式为system("CLS")。主要功能就是清空屏幕。主要是对DOS的操作,除了cls system函数还有如下功能
dir | 显示指定路径上所有文件或目录的信息 |
md | 建立目录 |
rd | 删除目录 |
cd | 进入指定目录 |
copy | 拷贝文件 |
del | 删除文件 |
ren | 改名 |
type | 显示文本文件 |
discopy | 磁盘复制 |
deltree | 删除目录树 |
mem | 查看你的计算机内存有多少,以及内存的使用情况 |
chkdsk | 检查你的磁盘的使用情况。 |
sys | 传递系统文件命令 |
pass | 设定DOS寻找.COM、.EXE、.BAT文件的所在目录 |
cls | 清除显示器屏幕上的内容,使DOS提示符到屏幕左上角 |
Time | 显示和设置DOS的系统时间 |
date | 显示和设置DOS的系统日期 |
ver | 显示正在运行的DOS系统版本号 |
表1:有关于DOS操作的相关命令
- Getchar:
getchar()是C语言中的函数,C++中也包含了该函数。getchar()函数的作用是从标准的输入stdin中读取字符。也就是说,getchar()函数以字符为单位对输入的数据进行读取。在控制台中通过键盘输入数据时,以回车键作为结束标志。当输入结束后,键盘输入的数据连同回车键一起被输入到输入缓冲区中。在程序中第一次调用getchar()函数从输入缓冲区中读取一个字节的数据。需要注意的是,如果此时在程序中第二次调用getchar()函数,因为此时输入缓冲区中还有回车键的数据没有被读出,第二个getchar()函数读出的是回车符。
3)math.h 数学函数库,一些数学计算的公式的具体实现是放在math.h里,具体有:
1 三角函数
double sin (double x); x的正弦值
double cos (double x); x的余弦值
double tan (double x); x的正切值
2 反三角函数
double asin (double x); 结果介于[-PI/2, PI/2],x值域为[-1,1]
double acos (double x); 结果介于[0, PI],x值域为[-1,1]
double atan (double x); 反正切(主值), 结果介于[-PI/2, PI/2]
double atan2 (double y, double x); 反正切(整圆值), 结果介于[-PI, PI]
3 双曲三角函数
double sinh (double x); x的双曲正弦值
double cosh (double x); x的双曲余弦值
double tanh (double x); x的双曲正切值
4 指数与对数
double exp (double x); 幂函数e^x
double pow (double x, double y); x^y,如果x=0且y<=0,或者x<0且y不是整型数,将产生定义域错误
double sqrt (double x); x的平方根,其中x>=0
double log (double x); 以e为底的对数,自然对数,x>0
double log10 (double x); 以10为底的对数,x>0
5 取整
double ceil (double x); 取上整
double floor (double x); 取下整
6 绝对值
double fabs (double x); x的绝对值
7 标准化浮点数
double frexp (double x, int *exp); 标准化浮点数, x = f * 2^exp, 已知x求f, exp ( x介于[0.5, 1] )并返回f值
double ldexp (double x, int exp); 与frexp相反, 已知x, exp求x*2^exp
8 取整与取余
double modf (double x, double *ip); 将参数的整数部分通过指针回传, 返回小数部分,整数部分保存在*ip中
double fmod (double x, double y); 返回两参数相除x/y的余数,符号与x相同。如果y为0,则结果与具体的额实现有关
综上所述,通过这次课程设计,增强了我的自信心。因为在这次课程设计中,我遇到了一些问题,但是都逐个得解决了,虽然有些问题请教了同学,但是从中学了很多东西,也学到了一些处理问题的方法。在能力上得到了一些提升。同时也养成了独立思考问题,以及和同学一起探索问题的良好习惯。当然,在课程设计过程中,有些细节的处理还是不够完美,需要完善的地方还有很多,还需要继续努力,尽量将程序完善。
uble fabs (double x); x的绝对值
7 标准化浮点数
double frexp (double x, int *exp); 标准化浮点数, x = f * 2^exp, 已知x求f, exp ( x介于[0.5, 1] )并返回f值
double ldexp (double x, int exp); 与frexp相反, 已知x, exp求x*2^exp
8 取整与取余
double modf (double x, double *ip); 将参数的整数部分通过指针回传, 返回小数部分,整数部分保存在*ip中
double fmod (double x, double y); 返回两参数相除x/y的余数,符号与x相同。如果y为0,则结果与具体的额实现有关
综上所述,通过这次课程设计,增强了我的自信心。因为在这次课程设计中,我遇到了一些问题,但是都逐个得解决了,虽然有些问题请教了同学,但是从中学了很多东西,也学到了一些处理问题的方法。在能力上得到了一些提升。同时也养成了独立思考问题,以及和同学一起探索问题的良好习惯。当然,在课程设计过程中,有些细节的处理还是不够完美,需要完善的地方还有很多,还需要继续努力,尽量将程序完善。
10、代码实现
#include <stdio.h>
#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <math.h>
#define pi 3.1415926
using namespace std;
/***************************************************************************************************************************************/
/***函数定义部分***/
void adorn(char adorn,int n);//装饰函数
int fun(char *c1, int s, int e);//判断括号是否匹配
void PrintList(int top,float a[]);//打印计算结果
char input();//从键盘中读取数据
char Change(char c1[]); //将中缀表达式转换为后缀表达式并进行计算
int main();//使系统可以在函数内调用main函数
char systemmenu();//打印系统用户界面
float easycalculate(float o,char p,float q);//简单计算器计算功能
int easymenu();//简单计算器的功能界面
/***************************************************************************************************************************************/
/***************************************************************************************************************************************/
/***定义三个字符串,以便实现栈操作 ***/
char c1[1024];//用于存储输入的中缀表达式
char c2[1024];//为栈用于转化过程中对数和操作符进行压栈
char c3[1024];//用于元素出栈后进行相关运算,并将最终结果进行重新压栈
/***************************************************************************************************************************************/
/***************************************************************************************************************************************/
/***定义全局变量***/
float a[1024];// 用于最后计算结果的输出使用
int count=0;
/***************************************************************************************************************************************/
/***************************************************************************************************************************************/
/***各子函数实现部分***/
int easymenu()//简单计算器的功能界面
{
float o;
char p;
float q;
float num;
scanf("%f%c%f",&o,&p,&q);
num=easycalculate(o,p,q);
cout<<o<<p<<q<<"="<<num<<endl;
cout<<"若想结束本次计算请输入‘$’号键"<<endl;
co: cout<<num;//显示第一位数字
o=num;
cin>>p;//用户只需要对运算符与数字进行输入即可
if(p=='$')
{
return 0;
}
else
{
cin>>q;
num=easycalculate(o,p,q);//对运算式进行再次运算
cout<<o<<p<<q<<"="<<num<<endl;
goto co;
}
}
float easycalculate(float o,char p,float q)//简单计算器计算功能
{
float i=0;
if(p=='+')
{
i=(o+q);
}
else if(p=='-')
{
i=(o-q);
}
else if(p=='*')
{
i=(o*q);
}
else if(p=='/')
{
i=(o/q);
}
return i;
}
char systemmenu()//打印系统用户界面
{
cout<<"欢迎您使用本系统"<<endl;
cout<<"本系统可实现的用户功能有:"<<endl;
cout<<endl;
cout<<"********************************************"<<endl;
cout<<"1、简单计算器(二元计算)"<<endl;
cout<<"2、中缀表达式转后缀表达式并计算";
cout<<"[此功能识别的的计算符号为'+''-''*''/''^''!']"<<endl;
cout<<"*输入其他数字退出本系统*"<<endl;
cout<<"********************************************"<<endl;
}
void adorn(char adorn,int n)//装饰函数
{
for(int i=0;i<n;i++)
{
putchar(adorn);//通过ASCLL码判断如果是数字则将ASCLL码-48转换为字符
}
putchar('\n'); //强制在字符串的结尾加上\n,方便计算字符串内的个数
}
int fun(char *c1, int s, int e)//判断括号是否匹配 (1匹配;0不匹配)
{
char L;
char R;
while((s<=e))
{
switch(c1[s])
{
case '(': L = c1[s];
R = ')';
break;
case '[': L = c1[s];
R = ']';
break;
case '{': L = c1[s];
R = '}';
break;
case ')': return 0;
case ']': return 0;
case '}': return 0;//若输入字符串为以上字符,则返回0
default:
L = '\0';
break;
}
if(c1[s] == L)
{
int p = 1;
int q=0;
int x = s+1;//x为s的下一个字符
while((x<=e)) // 搜索匹配的右括号
{
if(c1[x] == L)
p++;
if(c1[x] == R)
q++;
if(q>p)
return 0;
if(p == q) // 再对已匹配括号里面的括号进行匹配
{
if(fun(c1, s+1, x-1) == 0) // 递归调用,从最外层的括号分别向内匹配
return 0;
s=x;
break;
}
x++;
}
if(p>q)
return 0;//如果不匹配返回0;
}
s++;
}
return 1;//都匹配上返回1
}
void PrintList(int top,float a[])//打印计算结果
{
cout<<"计算结果为:"<<endl;
if(a[top]>1000||a[top]<0.001) //科学计数法
printf("%.3E\n",a[top]);
else if(a[top]<=1000||a[top]>=0.001)
printf("%.8f\n",a[top]);
adorn('-',90);
}
char input()//从键盘中读取数据
{
int count=0;
int i;
int j;
int m;
int n;
int top=0;
int v=-1;
int u=0;
fflush(stdin);
gets(c1);//输入c1
}
char Change(char c1[])//将中缀表达式转换为后缀表达式并进行计算
{
fflush(stdin);
int count=0;
int i;
int j;
int m;
int n;
int top=0;
int v=-1;
int u=0;
int k=strlen(c1);
int r= fun(c1, 0, k-1);
if(r != 1)
{
printf("**括号不匹配,请重新输入**:\n");
loop:
input();
if(*c1=='#')
{
main();//返回主函数
}
else
{
Change(c1);
}
}
else
{
for(i=0;i<k;i++)//对输入的数组进行后缀表达式转换
{
switch(c1[i])
{
case '(':
c2[++top]=c1[i];
break;
case '+':
case '-':
while(top>0 && c2[top]!='(')//判断栈顶的元素是否为括号,且栈不空时 ,将栈中的前两个元素进行出栈与运算,将运算的结果进行重新入栈
{
c3[++v]=c2[top];
c3[++v]=' ';
top--;
}
c2[++top]=c1[i]; //当栈空或者遇到(时,将"-"直接入栈
break;
case '*':
case '/':
while(top>0 && c2[top]!='(' && c2[top]!='+' && c2[top]!='-')//考虑优先级
{
c3[++v]=c2[top];
c3[++v]=' ';
top--;
}
c2[++top]=c1[i];
break;
case '%':
while(top>0 && c2[top]!='(' && c2[top]!='+' && c2[top]!='-')//考虑优先级
{
c3[++v]=c2[top];
c3[++v]=' ';
top--;
}
c2[++top]=c1[i];
break;
case '^':
while(top>0 && c2[top]!='(' && c2[top]!='+' && c2[top]!='-')//考虑优先级
{
c3[++v]=c2[top];
c3[++v]=' ';
top--;
}
c2[++top]=c1[i];
break;
case '!':
while(top>0 && c2[top]!='(' && c2[top]!='+' && c2[top]!='-')//考虑优先级
{
c3[++v]=c2[top];
c3[++v]=' ';
top--;
}
c2[++top]=c1[i];
break;
case ')':
while(c2[top]!='('){
c3[++v]=c2[top];
c3[++v]=' ';
top--;
}
top--;
break;
default:
c3[++v]=c1[i];
if(c1[i+1]>'9' || c1[i+1]<'0')
c3[++v]=' ';
break;
}
}
while(top>0 && c2[top]!='(')
{
c3[++v]=c2[top];
c3[++v]=' ';
top--;
}
cout<<"其后缀表达式为:"<<endl;
puts(c3);
top=0;
float sum;
k=strlen(c3);
for(i=0;i<k;i++)
{
if(c3[i]==' ')
{
;
}
else if(c3[i]=='+')
{
sum=a[top-1]+a[top];
a[--top]=sum;
}
else if(c3[i]=='-')
{
sum=a[top-1]-a[top];
a[--top]=sum;
}
else if(c3[i]=='*')
{
sum=a[top-1]*a[top];
a[--top]=sum;
}
else if(c3[i]=='/')
{
if(a[top]==0)
{
cout<<"**输入错误,被除数不能为0,请重新输入**:"<<endl;
goto loop;
}
else
{
sum=a[top-1]/a[top];
a[--top]=sum;
}
}
else if(c3[i]=='%')
{
if(a[top]==0)
{
cout<<"**输入错误,被取余数不能为0,请重新输入**:"<<endl;
goto loop;
}
else
{
int sum1=a[top-1]*10;
int sum2=a[top]*10;
sum=(sum1%sum2)/10;
a[--top]=sum;
}
}
else if(c3[i]=='^')
{
sum=pow(a[top-1],a[top]);
a[--top]=sum;
}
else if(c3[i]=='!')
{
if(a[top]<0)
{
cout<<"**输入错误,负数没有阶乘,请重新输入**:"<<endl;//提示信息,并重新输入
input();
goto loop;
}
else
{
float sum=1;
for(int j=1;j<=a[top];j++)
{
sum=sum*j;
}
a[top]=sum;
}
}
else
{
int m=0;
while(c3[i]>='0' && c3[i]<='9')
{
m=10*m+c3[i]-'0';
i++;
}
a[++top]=m;
}
}
PrintList(top,a);
}
}
/***************************************************************************************************************************************/
/***************************************************************************************************************************************/
/***主函数部分***/
int main()
{
while(1)
{
open:
system("cls");
systemmenu();
int n;
cin>>n;
switch(n)
{
case 1: system("cls");
while(1)
{
cout<<"欢迎来到简单计算器功能"<<endl;
cout<<"本功能支持'+''-''*''/'四种运算符号";
cout<<"[且运算符号需为英文字符]"<<endl;
easymenu();
int flag;
cout<<"*************************"<<endl;
cout<<"是否继续简单计算器功能?"<<endl;
cout<<"1-YES"<<endl;
cout<<"2-NO"<<endl;
cout<<"*************************"<<endl;
cin>>flag;
if(flag==2)
{
break;
goto open;
}
}
break;
case 2: system("cls");//实现启动器的DOS功能,清除显示器屏幕上的内容,使DOS提示符到屏幕左上角
while(1)
{
fflush(stdin);//清除缓冲区的值
int count=0;
int i;
int j;
int m;
int n;
int top=0;
int v=-1;
int u=0;
adorn('-',90);
cout<<"**请输入需要计算的中缀表达式,直接回车即可,注意本系统只识别英文符号**:" <<endl;
cout<<"如果想退出本界面,请输入‘#’号键"<<endl;
input();
if(*c1=='#')
{
break;
goto open;
}
else
{
Change(c1);
}
}
break;
default:system("cls");
cout<<"**************"<<endl;
cout<<"感谢您的使用!" <<endl;
cout<<"我们下次再会!"<<endl;
cout<<"**************"<<endl;
goto over;
}
}
over:
return 0;
}
/***************************************************************************************************************************************/
标签:中缀,int,double,top,C++,括号,表达式,函数 来源: https://blog.csdn.net/yaoyimingde/article/details/121993315