一、需求分析
项目:简单计算器。
请按照四则运算加、减、乘、除、幂(^)和括号的优先关系和惯例,编写计算器程序。
要求:
① 从键盘输入一个完整的表达式,以回车作为表达式输入结束的标志。
② 输入表达式中的数值均为大于等于零的整数。中间的计算过程如果出现小数也只取整。
例如,
输入:4+2*5=
输出:14
输入:(4+2)*(2-10)=
输出:-48
二、概要设计
- 程序中用到的抽象数据类型的定义:
typedef struct
{ //数字栈结构体
int data[MAXSIZE];
int top;
}Snum;
typedef struct
{ //符号栈结构体
char op[MAXSIZE];
int top;
}Sop;
- 主程序的流程:
首先读取表达式,判断表达式是否合理。如果不合理,则输出“表达式输入有误!”如果合理,则初始化两个栈,一个是数字栈存放数字,一个是符号栈存放运算符号,再使用Calculator()函数计算表达式,得出结果。
int main()
{
char e[MAXSIZE]; //定义一个数组存放表达式
fgets(e, MAXSIZE-1, stdin); //读入表达式
if(!judge(e))
{
Snum *n; //数字栈
Sop *o; //符号栈
InitSnum(&n); //初始化数字栈和符号栈
InitSop(&o);
//表达式合理则计算
Calculator(n, o, e);
return 0;
}
else
{
printf("表达式输入有误!\n");
return 0;
}
}
三、详细设计
核心算法是Calculator()函数,输入数字栈和符号栈,以及需要计算的表达式,依次读取表达式中的字符
①当遇到空格和左括号时跳过,左括号存入符号栈;
②用isdigit函数判断是否是数字,存入数字栈;
③如果不是数字,是右括号,因为已经判断表达式合理,所以直接计算,当符号栈顶不是左括号,说明有其他计算符号,出栈并将数字栈的上面两个数字进行运算,再将运算结果存入数字栈顶,最后将左括号出栈。
④当e[i]不是以上三种情况,说明是运算符号,比较其与运算符栈顶符号优先级,如果e更高级,则直接入栈,否则将符号栈顶元素出栈并运算。即要先计算优先级高的运算。
存放完e[i]后,检查运算符栈,如果不为空,则出栈计算。
void Calculator(Snum *n, Sop *o, char *e)
{
int i;
//当遇到空格和左括号跳过
for(i = 0; i < strlen(e) && e[i] != '='; i++)
{
if(e[i] == ' ') continue;
if(e[i] == '(') o->op[++o->top] = e[i];
else if(isdigit(e[i])) //用isdigit函数判断是否是数字
{
int a = 0;
while(i < strlen(e) && isdigit(e[i])) //输入多位数
{
a = (a * 10) + (e[i] - '0');
i++;
}
i--;
n->data[++n->top] = a;
}
else if(e[i] == ')')
{
while(o->top!=-1 && o->op[o->top] != '(')
{
int a2 = n->data[n->top--];
int a1 = n->data[n->top--];
char opn = o->op[o->top--];
n->data[++n->top] = Op(a1, a2, opn);
}
if(o->top != -1) o->top--;
}
else
{
Priorityop(n, o, e[i]);
o->op[++o->top] = e[i];
}
}
while(o->top != -1)
{
int a2 = n->data[n->top--];
int a1 = n->data[n->top--];
char opn = o->op[o->top--];
n->data[++n->top] = Op(a1, a2, opn);
}
printf("%d\n", n->data[n->top]);
}
其他函数:
- 运算函数,计算简单加减乘除乘方的值:
Status Op(int a, int b, char op)
{ //运算
switch(op)
{
case '+': return a + b;
case '-': return a - b;
case '*': return a * b;
case '/': return b ? a / b : 0;
case '^': return (int)pow(a, b);
default: return 0;
}
}
- 判断符号优先级函数,以便判断运算符号是否入符号栈。并判断是否进行运算。
Status Priority(char op)
{ //判断符号优先级
if(op == '+' || op == '-') return 1;
if(op == '*' || op == '/') return 2;
if(op == '^') return 3;
return 0;
}
Status Priorityop(Snum *n, Sop *o, char e)
{ //比较符号优先级,如果e更高级,则直接入栈,
//否则将符号栈顶元素出栈并运算
while(Priority(o->op[o->top]) >= Priority(e))
{
int a2 = n->data[n->top--];
int a1 = n->data[n->top--];
char opn = o->op[o->top--];
n->data[++n->top] = Op(a1, a2, opn);
}
}
四、调试分析
不足:该程序一开始并没有设置算法来判断输入表达式是否合法,当输入不合法表达式,如:
①输入不合法字符
如中文字符、英文字母、其他特殊符号,即在{+, -, *, /, ^, (, )}和 0-9 数字之外的其他字符,均无输出。
输入:a+1=
输出:
②输入不合法运算符号
如括号不匹配,+ - * / 位置不对,均输出错误。
所以需要函数来判断输入表达式是否合法。
//判断表达式是否合理
Status judge(char e[])
{
if(e[0] == '/' || e[0] == '*')
return 0;
if(e[strlen(e)-1] < '0' && e[strlen(e)-1 > '9'])
return 0;
int flag = 0; //记录括号数量
for(int i = 0; i < strlen(e); i++)
{
if(e[i] == '(')
{
if(i == 0 && (e[i+1]=='*' || e[i+1] == '/'))
return 0;
else if(e[i-1]>='0' && e[i-1]<='9')
return 0;
flag++;
}
else if(e[i]==')')
{
if(i == 0) return 0;
else if(e[i-1] == '+' || e[i-1]=='*' || e[i-1]=='-' || e[i-1]=='/')
return 0;
else if(e[i+1]>='0' && e[i+1]<='9')
return 0;
flag--;
}
}
if(flag == 0) return 1;
return 0;
}
五、测试结果
附录
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>
#define MAXSIZE 100
typedef int Status;
typedef struct
{ //数字栈结构体
int data[MAXSIZE];
int top;
}Snum;
void InitSnum(Snum **N)
{ //初始化数据栈
*N = (Snum *)malloc(sizeof(Snum));
(*N)->top = -1;
}
typedef struct
{ //符号栈结构体
char op[MAXSIZE];
int top;
}Sop;
void InitSop(Sop **o)
{ //初始化符号栈
*o = (Sop *)malloc(sizeof(Sop));
(*o)->top = -1;
}
//判断表达式是否合理
Status judge(char e[])
{
if(e[0] == '/' || e[0] == '*' || e[0] == '+' || e[0] == '-')
return 0;
if(e[strlen(e)-2] < '0' && e[strlen(e)-2 > '9'])
return 0;
int flag = 0; //记录括号数量
for(int i = 0; i < strlen(e); i++)
{
if(e[i] == '(')
{
if(i == 0 && (e[i+1]=='*' || e[i+1] == '/'))
return 0;
else if(e[i-1]>='0' && e[i-1]<='9')
return 0;
flag++;
}
else if(e[i]==')')
{
if(i == 0) return 0;
else if(e[i-1] == '+' || e[i-1]=='*' || e[i-1]=='-' || e[i-1]=='/')
return 0;
else if(e[i+1]>='0' && e[i+1]<='9')
return 0;
flag--;
}
}
if(flag == 0) return 1;
return 0;
}
Status Op(int a, int b, char op)
{ //运算
switch(op)
{
case '+': return a + b;
case '-': return a - b;
case '*': return a * b;
case '/': return b ? a / b : 0;
case '^': return (int)pow(a, b);
default: return 0;
}
}
Status Priority(char op)
{ //判断符号优先级
if(op == '+' || op == '-') return 1;
if(op == '*' || op == '/') return 2;
if(op == '^') return 3;
return 0;
}
Status Priorityop(Snum *n, Sop *o, char e)
{ //比较符号优先级,如果e更高级,则直接入栈,
//否则将符号栈顶元素出栈并运算
while(Priority(o->op[o->top]) >= Priority(e))
{
int a2 = n->data[n->top--];
int a1 = n->data[n->top--];
char opn = o->op[o->top--];
n->data[++n->top] = Op(a1, a2, opn);
}
}
void Calculator(Snum *n, Sop *o, char *e)
{
int i;
for(i = 0; i < strlen(e) && e[i] != '='; i++)
{
if(e[i] == ' ') continue;
if(e[i] == '(') o->op[++o->top] = e[i];
else if(isdigit(e[i])) //用isdigit函数判断是否是数字
{
int a = 0;
while(i < strlen(e) && isdigit(e[i])) //输入多位数
{
a = (a * 10) + (e[i] - '0');
i++;
}
i--;
n->data[++n->top] = a;
}
else if(e[i] == ')')
{
while(o->top!=-1 && o->op[o->top] != '(')
{
int a2 = n->data[n->top--];
int a1 = n->data[n->top--];
char opn = o->op[o->top--];
n->data[++n->top] = Op(a1, a2, opn);
}
if(o->top != -1) o->top--;
}
else
{
Priorityop(n, o, e[i]);
o->op[++o->top] = e[i];
}
}
while(o->top != -1)
{
int a2 = n->data[n->top--];
int a1 = n->data[n->top--];
char opn = o->op[o->top--];
n->data[++n->top] = Op(a1, a2, opn);
}
printf("%d\n", n->data[n->top]);
}
int main()
{
char e[MAXSIZE]; //定义一个数组存放表达式
fgets(e, MAXSIZE-1, stdin); //读入表达式
if(judge(e))
{
Snum *n; //数字栈
Sop *o; //符号栈
InitSnum(&n); //初始化数字栈和符号栈
InitSop(&o);
//表达式合理则计算
Calculator(n, o, e);
return 0;
}
else
{
printf("表达式输入有误!\n");
return 0;
}
}
注:初学者,借鉴许多前辈经验,欢迎指正交流!