使用栈的四则运算
1.题目描述
用顺序栈和算符优先法求解表达式的值
2.任务定义和问题分析
四则运算基本运要通过算法优先级和后缀表达式的思想完成,应当由以下功能:
(1)可以先求解后缀表达式。然后再求值,也可以一并完成。
(2)表达式中的运算为加、减、乘、除四种,包括括号,参与运算的数字为个位数。
(3)要考虑除法运算除数为零的特殊情况,有相应的处理措施。
(4)能检测括号不匹配的情况。
(5)建立操作符栈和操作数栈,编写相应的进栈和出栈函数。
(6)要有任务分解逐步求精的意识,合理划分子函数实现单个功能。
(7)可以进行多次表达式的计算,而不是运算一次就结束。
(7)操作数可以为多位整数,或者小数。例如123(2.3+89.5)。
(8)可增加阶乘运算,例如3!+42/(1-5)。
(9)可增加指数运算,例如4*2/(1-5)^2。
分析问题:分析四则运算需要用顺序栈来完成计算。因为我们只在一端输入输出,使用顺序栈使用数组下标进行进栈出栈操作更方便快捷,且由于一次性分配了足够的内存,时间处理上更快。而为防止栈的溢出,我使用动态分配的顺序栈。各具体功能设计为子函数,方便调试和复用,同时程序结构清晰易读。
简单的逻辑结构:定义一个char类型的字符组作为字符栈,一个int类型的数组作为数字栈。各子函数中,根据具体情形进行异常处理并给出提示。(像输入错误格式的算术式时会给出恰当的提示)。
3.数据结构的选择和概要设计
一.为了实现上述程序功能,首先定义存储的字符的字符栈以及数字栈。
二.系统中的子函数及功能要求
程序中共有14个子函数,分别为:
①边将算术式转为后缀表达式边计算的函数option()
②寻找大数小数的函数find()
③进数字栈的函数input_number()
④判断数字栈是否满了的函数Fulli()
⑤判断字符栈是否满并存入一个字符的函数Storage()
⑥判断字符栈是否满的函数 fullc()
⑦将一个字符存入字符栈input_char()
⑧使用弹出的运算符的函数Operation_And_Storage()
⑨将一个数字出栈的函数output_number()
⑩进行实际运算的函数shuan()
⑪弹出字符的函数output_number()
⑫计算阶乘的函数jie()
⑬为字符栈增添空间的函数addstackc()
⑭为数字栈增添空间Addstacki()
各子函数具体介绍如下:
(1)double option(char a[],char_stack b,int_stack c)
①输 入:无
②输 出:无
③返回值:输入错误的提示ERROR 或 计算的值
④功 能:边将算术式转为后缀表达式边计算
(2)double find(char a[],int *r,int i)
①输 入:无
②输 出:无
③返回值:输入错误的提示ERROR 或找到的数
④功 能:使四则运算可以计算小数和大于10的自然数
(3)void input_number(double a,int_stack c)
①输 入:无
②输 出:无
③返回值:无
④功 能:将一个数存入数字栈
(4)void fulli(int_stack c)
①输 入:无
②输 出:无
③返回值:无
④功 能:判断数字栈是否满了
(5)void Storage(char a,char_stack b)
①输 入:无
②输 出:无
③返回值:无
④功 能:判断字符栈是否满并存入一个字符
(6)void fullc(char_stack b)
①输 入:无
②输 出:无
③返回值:无
④功 能:判断字符栈是否满
(7)Void input_char(a,b)
①输 入:无
②输 出:无
③返回值:无
④功 能:将一个字符存入字符栈
(8)double Operation_And_Storage(char_stack b,int_stack c)
①输 入:无
②输 出:无
③返回值:输入错误的提示ERROR 或 1
④功 能:弹出两个数字栈,弹出一个字符栈,根据字符栈计算,就计算结果存入数字栈
(9)double output_number(int_stack c)
①输 入:无
②输 出:无
③返回值:出栈的数字
④功 能:将数字栈出一个栈
(10)double shuan(double a,double b,char c)
①输 入:无
②输 出:无
③返回值:计算的结果 或 输入错误的提示ERROR
④功 能:实际计算操作
(11)char out_char(char_stack b)
①输 入:无
②输 出:无
③返回值:弹出的字符
④功 能:将一个字符弹出字符栈
(12)double jie(int a)
①输 入:无
②输 出:无
③返回值:阶乘的值
④功 能:计算阶乘
(13)void addstackc(char_stack b)
①输 入:无
②输 出:无
③返回值:无
④功 能:为字符栈增添空间
(14)void addstacki(int_stack c)
①输 入:无
②输 出:无
③返回值:无
④功 能:为数字栈增添空间
三.主程序及各程序模块(函数)之间的层次(调用)关系
4.详细设计
(一)double option(char a[],char_stack b,int_stack c)
我将遇到的字符分为几大类,分别是数字,(,),+或-,*或/,!,^.7种情况。并根据算术优先顺序对每大类进行分类讨论。由于分类多但每个类别进行的操作都简单不适合用文字赘述,就看我的逻辑框架。
逻辑框架:
流程图:
代码与注释:
(二)double find(char a[],int (*)r,int i)(由于博客园不显示我的指针符*,所以我只好加了个括号框起来,你自己写不要框)
通过循环判断大数或小数是多少。一直取a[i]的下一个字符,如果是数字就让本来的数字10加上该数字成为新的本来数字,直到下一个不是数字。如果这个不是数字的字符恰好是“.”小数点,就一直取a[i]的下一个字符,让本来数字加上该数字pow(10,-w),w为小数点后的几位。直到下次不是数字。
5.调试分析
问题(1):
问题概述:一开始我的主要逻辑程序全部写在一起,像缠绕在一起的数据线,杂乱无章。当我想要完成进阶任务时,我发现自己的程序不具有可维护性和提升性。
解决过程:我先画了个简单的思维导图,将重复的部分写成专门的子函数,将自己的程序,重新写了一次。
问题(2):
问题概述:在遇到‘)’后即使后面还有未输出的字符,但我的字符栈就不输出也不输入了,而且我的检查程序还没有报错ERROR
解决过程:我用printf将循环中每个字符栈的字符以及其拥有的字符个数打印出来,进行调试。发现‘)’后字符栈的下一个进栈点错误的出现在-1处,由于我的判断语句只判断了前一个字符的种类是什么以及是不是在0处,没有考虑会出现小于0的情况。所以经过‘)’后就不进入判断语句。就造成了没有报错但输出有错的情况。
解决方法:字符栈本不可能在-1处。我发现在子函数中我已经x-1了,但主函数的循环里我又-1,两次操作重复了,于是出现了负数。所以我删去了循环的-1。
6.测试结果和分析
7. 源代码(完成了去年说的要改进的计划,去年没学栈但老师让我们写四则运算的苦逼场景还历历在目,但在坚持2天后还真写了一个四则运算的程序(虽然他不能计算括号,阶乘,次幂但它可以做到理论上无限的加减乘除,支持小数大数),收获感满满,就写了一个不太成熟的博客并说之后会完善他,今天就完善了。)
点击查看代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#define ERROR 3.14159
typedef struct stack1 { //字符栈ser
char *ser;//存储算数符号
int x;//指向栈位置
int max;//表示这个栈最大存储的位置
} stack1,*char_stack;
typedef struct stack2 { //数字栈seri
double *ser;//存储算数的数字
int x;//指向栈位置
int max;//表示这个栈最大存储的位置
} stack2,*int_stack;
double option(char a[],char_stack b,int_stack c);//进出栈的计算顺序
double shuan(double a,double b,char c);//实际计算操作
void Storage(char a,char_stack b);//存入数字栈
double Operation_And_Storage(char_stack b,int_stack c);//弹出两个数字栈,弹出一个字符栈,根据字符栈计算,就计算结果存入数字栈
void input_number(double a,int_stack c);//进栈 数栈
double output_number(int_stack c);//出栈 数栈
void fulli(int_stack c);//判断数栈是否满
void addstacki(int_stack c);//添加额外空间
void input_char(char a,char_stack b);//进栈 字符栈
char out_char(char_stack b);//出栈 字符栈
void fullc(char_stack b);//判断字符栈是否满
void addstackc(char_stack b);//添加字符栈额外空间
double jie(int a);//计算阶乘
double find(char a[],int *r,int i);//找到大数或小数
int main()//主函数
{
char_stack charstack = (char_stack)malloc(sizeof(stack1));//初始化一个字符栈,一个数字栈
charstack->ser=(char*)malloc(100*sizeof(char));
charstack->x=0;
charstack->max=100;
int_stack intstack =(int_stack)malloc(sizeof(stack2));
intstack->ser=(double*)malloc(100*sizeof(double));
intstack->x=0;
intstack->max=100;
char a[100];
int count_times;
printf("请输入你想进行几次运算(少于等于十次):");
while(1)
{
scanf("%d",&count_times);
if(count_times<=10&&count_times>=0)
{
break;
}
printf("请输入你想进行几次运算(少于等于十次):");
}
getchar();
int i;
double result;
for(i=0; i<count_times; i++) {
printf("请输入要计算的式子(如果要用括号记得用英文的喔):");
fgets(a,sizeof(a),stdin);
result=option(a,charstack,intstack);
charstack->x=0;
intstack->x=0;
if(result==ERROR)
{
printf("输入的算术式不符合格式\n");
}
else
{
printf("结果是:\n");
printf("%.2f\n",result);
}
}
free(charstack->ser);
free(intstack->ser);
free(charstack);
free(intstack);
return 0;
}
double find(char a[],int *r,int i)//存入大数或小数
{
int j=i+1;
int w=0;
double asum=(double)a[i]-48;
for(;a[j]>=48&&a[j]<=57;j++)
{
asum=asum*10+((double)a[j]-48);
}
if(a[j]=='.')
{
j++;
w--;
if(!(a[j]>=48&&a[j]<=57))
{
return ERROR;
}
for(;a[j]>=48&&a[j]<=57;j++,w--)
{
asum+=((double)a[j]-48)*pow(10,w);
}
}
*r=j-1;
return asum;
}
double jie(int a)//计算阶乘
{
if(a==0||a==1)
{
return 1;
}
double i,sum=1;
for(i=1;i<=a;i++)
{
sum*=i;
}
return sum;
}
//
void addstackc(char_stack b) //添加字符栈额外空间
{
b->ser=(char *)realloc(b->ser,(b->max+5)*sizeof(char));
b->max+=5;
}
void fullc(char_stack b)//判断字符栈是否满
{
if(b->x==b->max-2)
{
addstackc(b);
}
}
char out_char(char_stack b)//出栈 字符栈
{
b->x--;
return b->ser[b->x];
}
void input_char(char a,char_stack b)//进栈 字符栈
{
b->ser[b->x]=a;
b->x++;
}
double output_number(int_stack c)//出栈 数栈
{
c->x--;
return c->ser[c->x];
}
void fulli(int_stack c)//判断数栈是否满
{
if(c->x==c->max-2)
{
addstacki(c);
}
}
void addstacki(int_stack c)//添加数栈额外空间
{
c->ser=(double *)realloc(c->ser,(c->max+5)*sizeof(double));
c->max+=5;
}
void input_number(double a,int_stack c)//进栈 数栈
{
c->ser[c->x]=a;
c->x++;
}
//
double shuan(double a,double b,char c)//实际计算操作
{
if(c=='+')
{
return a+b;
}
else if(c=='-')
{
return a-b;
}
else if(c=='*')
{
return a*b;
}
else if(c=='/'&&b==0)
{
return ERROR;
}
else if(c=='/'&&b!=0)
{
return a/b;
}
else if(c=='^')
{
return pow(a,b);
}
else
{
return ERROR;
}
}
void Storage(char a,char_stack b)//存入字符栈
{
fullc(b);
input_char(a,b);
}
double Operation_And_Storage(char_stack b,int_stack c)//弹出两个数字栈,弹出一个字符栈,根据字符栈计算,就计算结果存入数字栈
{
double p,q,n;
char w;
if(c->x==0)
{
return ERROR;
} else
{
p=output_number(c);
}
if(c->x==0)
{
return ERROR;
} else
{
q=output_number(c);
}
w=out_char(b);
n=shuan(q,p,w);
if(n==ERROR)
{
return ERROR;
}
input_number(n,c);
return 1;
}
double option(char a[],char_stack b,int_stack c)//判断出栈还是入栈,并计算的基本逻辑
{
int i,x,y;
double w,num;
for(i=0; a[i]!='\0'; i++) {
if(a[i]>=48&&a[i]<=57) {
fulli(c);
num=(double)a[i]-48;
if((a[i+1]>=48&&a[i+1]<=57)||(a[i+1]=='.')) //若读取到数字
{
num=find(a,&i,i);
if(num==ERROR) {
return ERROR;
}
}
input_number(num,c);
}
else if(a[i]=='(') //若读取到(
{
Storage(a[i],b);
}
else if(a[i]=='+'||a[i]=='-') //若读取到+或-
{
if(b->x==0||b->ser[b->x-1]=='(')
{
Storage(a[i],b);
}
else if(b->ser[b->x-1]=='+'||b->ser[b->x-1]=='-'||b->ser[b->x-1]=='*'||b->ser[b->x-1]=='/'||b->ser[b->x-1]=='^')
{
if(Operation_And_Storage(b,c)==ERROR)
{
return ERROR;
}
input_char(a[i],b);
}
}
else if(a[i]=='*'||a[i]=='/') //若读取到*或/
{
if(b->x==0||b->ser[b->x-1]=='('||b->ser[b->x-1]=='+'||b->ser[b->x-1]=='-')
{
Storage(a[i],b);
}
else if(b->ser[b->x-1]=='*'||b->ser[b->x-1]=='/'||b->ser[b->x-1]=='^')
{
if(Operation_And_Storage(b,c)==ERROR)
{
return ERROR;
}
input_char(a[i],b);
}
}
else if(a[i]==')') //若读取到)
{
int j=b->x-1;
for(;j>=0&&b->ser[j]!='(';j--)
{
if(Operation_And_Storage(b,c)==ERROR)
{
return ERROR;
}
}
if(j<0)
{
return ERROR;
}
else
{
b->x--;
}
}
else if(a[i]=='!') //若读取到!
{
double q;
int ex;
q=output_number(c);
if(q<0)
{
return ERROR;
}
ex=(int)q;
if(ex!=q)
{
return ERROR;
}
q=jie(ex);
input_number(q,c);
}
else if(a[i]=='^') //若读取到^
{
Storage(a[i],b);
}
else if(a[i]=='.') //若读取到.
{
return ERROR;
}
}
for(;b->x>0;) //将字符栈依次出栈,并计算
{
if(Operation_And_Storage(b,c)==ERROR)
{
return ERROR;
}
}
if((c->x)>1) //如果字符栈完全计算完了且数字栈还有2个及以上的数字
{
return ERROR;
}
else //字符栈完全计算完了且数字栈只有一个数字
{
w=output_number(c);
return w;
}
}
8.寄语
这次博客不是老师的任务,倒是给过去的自己一个交代吧。前方道阻且长啊,HURRY up!!
标签:字符,2024.4,ser,int,double,四则运算,char,15,stack From: https://www.cnblogs.com/618907814lyy/p/18135481