写在前面
2020/07/17~2020/09/07是暑假期间,2020/08/24 ~ 2020/08/29学校组织了线上实训,2020/09/12是线下实训的辅导与答辩,我们组选择的是模拟计算器。其实当初选择这个的原因的因为我以前写过这个项目类似的OJ的例题:HNUCM-OJ1232算法3-4表达式求值,中缀转后缀表达式求值
想知道更多关于这个项目的知识可以去这个博客看一下。
项目代码:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
// 宏定义数组的最大长度
#define cmax 100010
// 定义浮点数的绝对误差
const double AbsErr = 1e-6;
// 定义储存中间及最后的运算结果的结构体StackNum
typedef struct
{
double data[cmax];
int top;
} StackNum;
// 定义储存中间及最后的运算结果的栈s1
StackNum s1;
// 定义存储运算符的结构体StackOperation
typedef struct
{
char data[cmax];
int top;
} StackOperation;
// 定义存储运算符的栈s2
StackOperation s2;
// 定义运算符的优先级的字符数组priority
// 通过二维数组判断运算符的优先级
// 其中'0'表示该种关系不会存在
char priority[20][20] =
{
{'>', '>', '>', '>', '>', '>', '>', '<', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>'}, //'!'
{'<', '>', '>', '<', '<', '<', '<', '<', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>'}, //'+'
{'<', '>', '>', '<', '<', '<', '<', '<', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>'}, //'-'
{'<', '>', '>', '>', '>', '<', '>', '<', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>'}, //'*'
{'<', '>', '>', '>', '>', '<', '>', '<', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>'}, //'/'
{'<', '>', '>', '>', '>', '>', '>', '<', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>'}, //'^'
{'<', '>', '>', '<', '<', '<', '<', '<', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>'}, //'%'
{'<', '<', '<', '<', '<', '<', '<', '<', '=', '<', '<', '<', '<', '<', '<', '<', '<', '0'}, //'('
{'>', '>', '>', '>', '>', '>', '>', '0', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>'}, //')'
{'<', '<', '<', '<', '<', '<', '<', '<', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>'}, //'>'
{'<', '<', '<', '<', '<', '<', '<', '<', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>'}, //'<'
{'<', '<', '<', '<', '<', '<', '<', '<', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>'}, //'>=' // 用'X'表示
{'<', '<', '<', '<', '<', '<', '<', '<', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>'}, //'<=' // 用'Y'表示
{'<', '<', '<', '<', '<', '<', '<', '<', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>'}, //'==' // 用'Z'表示
{'<', '<', '<', '<', '<', '<', '<', '<', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>'}, //'!=' // 用'W'表示
{'<', '<', '<', '<', '<', '<', '<', '<', '>', '<', '<', '<', '<', '<', '<', '>', '>', '>'}, //'&&' // 用'&'表示
{'<', '<', '<', '<', '<', '<', '<', '<', '>', '<', '<', '<', '<', '<', '<', '>', '>', '>'}, //'||' // 用'|'表示
{'<', '<', '<', '<', '<', '<', '<', '<', '0', '<', '<', '<', '<', '<', '<', '<', '<', '='}, //'#'
};
// getIndex用于获取每一个运算符在priority数组里面的下标
int getIndex(char c)
{
int index;
switch (c)
{
case '!':
index = 0;
break;
case '+':
index = 1;
break;
case '-':
index = 2;
break;
case '*':
index = 3;
break;
case '/':
index = 4;
break;
case '^':
index = 5;
break;
case '%':
index = 6;
break;
case '(':
index = 7;
break;
case ')':
index = 8;
break;
case '>':
index = 9;
break;
case '<':
index = 10;
break;
case 'X':
index = 11;
break;
case 'Y':
index = 12;
break;
case 'Z':
index = 13;
break;
case 'W':
index = 14;
break;
case '&':
index = 15;
break;
case '|':
index = 16;
break;
case '#':
index = 17;
break;
}
return index;
}
// getPriority用于获取栈顶元素与即将入栈的元素的优先级
char getPriority(char a, char b)
{
int index1 = getIndex(a);
int index2 = getIndex(b);
return priority[index1][index2];
}
// calculate直接计算中间表达式结果
double calculate(double num1, char c, double num2)
{
double Num;
switch (c)
{
case '+':
Num = num2 + num1;
break;
case '-':
Num = num2 - num1;
break;
case '*':
Num = num2 * num1;
break;
case '/':
Num = num2 / num1;
break;
case '^':
Num = pow(num2, num1);
break;
case '%':
Num = fmod(num2, num1);
break;
case '>':
{
if (num2 > num1 + AbsErr)
Num = 1;
else
Num = 0;
}
case '<':
{
if (num2 < (num1 + AbsErr))
Num = 1;
else
{
Num = 0;
}
}
case 'X':
{
if (num2 >= (num1 + AbsErr))
Num = 1;
else
Num = 0;
break;
}
case 'Y':
{
if (num2 <= (num1 - AbsErr))
Num = 1;
else
Num = 0;
break;
}
case 'Z':
{
if (fabs(num2 - num1) <= AbsErr)
Num = 1;
else
Num = 0;
break;
}
case 'W':
{
if (fabs(num2 - num1) > AbsErr)
Num = 1;
else
Num = 0;
break;
}
case '|':
{
if (num1 == 0 && num2 == 0)
Num = 0;
else
Num = 1;
break;
}
case '&':
{
if (num2 != 0 && num1 != 0)
Num = 1;
else
Num = 0;
break;
}
}
return Num;
}
// getAnswer主要函数,用于计算结果的主要函数
double getAnswer(char s[])
{
s2.data[++s2.top] = '#'; // 初始化栈2便于运算符之间的计算
int len = strlen(s);
int i = 0;
int counter = 0; // 作为数字字符是有一位还是多位的形式
int flag = 0; // 小数标记
while (i < len)
{
if (s[i] == '.') // 优先处理小数,flag==1则是小数情况
{
flag = 1;
counter = 0;
i++;
}
if (s[i] >= '0' && s[i] <= '9') // 如果是数值则进栈1
{
if (flag == 1) // 小数情况
{
if (counter == 0) // 小数点后的第一位
{
/*先将s1栈顶元素出栈,加上刚刚那个s[i]的小数位,再入栈*/
double temp = s1.data[s1.top--];
temp = temp + 0.1 * (s[i] - '0');
s1.data[++s1.top] = temp;
counter++;
i++;
}
else
{
int m = counter + 1; // m表示该数字s[i]为小数点后的第m位
/*先将s1栈顶元素出栈,加上刚刚那个s[i]的小数位,再入栈*/
double temp = s1.data[s1.top--];
temp = temp + pow(0.1, m) * (s[i] - '0');
s1.data[++s1.top] = temp;
counter++;
i++;
}
}
else // 非小数情况
{
if (counter == 0) // 该数字为第一位
{
s1.data[++s1.top] = s[i] - '0'; //进栈,并将字符型转化为整形
counter++;
i++;
}
else // 多位数进栈
{
/*先将s1栈顶元素出栈并且乘以10,加上刚刚那个s[i]的数位,再入栈*/
double temp = s1.data[s1.top--];
temp = temp * 10 + (s[i] - '0');
s1.data[++s1.top] = temp;
counter++;
i++;
}
}
}
else
{
counter = 0; // 计数器归零
flag = 0; // 小数标记器归零
// 比较s2.data[s2.top]和s[i]的优先级
char ch = getPriority(s2.data[s2.top], s[i]);
switch (ch)
{
// s2.data[s2.top]优先级小于s[i]的优先级
case '<':
{
// 运算符直接进栈
s2.data[++s2.top] = s[i];
i++;
break;
}
// s2.data[s2.top]优先级大于s[i]的优先级
case '>':
{
// 当前运算符出栈
char c = s2.data[s2.top--];
if (c == '!') // 如果是!(即 非 逻辑符时,则只需数值栈出栈一次即可)
{
// 数值栈栈顶元素出栈
double num1 = s1.data[s1.top--];
if (!num1)
s1.data[++s1.top] = 0;
else
s1.data[++s1.top] = 1;
}
else // 如果是其他符号时,数值栈连续出栈两次
{
double num1 = s1.data[s1.top--];
double num2 = s1.data[s1.top--];
// 计算新的值入栈
double MidNum = calculate(num1, c, num2);
s1.data[++s1.top] = MidNum;
}
break;
}
case '=':
{
s2.top--;
i++;
break;
}
}
}
}
return s1.data[s1.top];
}
int main()
{
char s[cmax];
memset(s, '\0', sizeof(s));
printf("*-----------------该程序可以实现基本表达式的计算-----------------*\n\n");
printf("*----------------------------------------------------------------*\n");
printf("* 请输入你想要进行的操作 *\n");
printf("* *\n");
printf("* 1.算术表达式计算 *\n");
printf("* *\n");
printf("* 2.关系表达式计算 *\n");
printf("* *\n");
printf("* 3.逻辑表达式计算 *\n");
printf("* *\n");
printf("* 0.退出该程序 *\n");
printf("* *\n");
printf("*----------------------------------------------------------------*\n");
printf("\n");
int n;
while (~scanf("%d", &n))
{
switch (n)
{
case 1:
{
printf("请输入算术表达式(以'#'表示结束):\n");
scanf("%s", s);
if(s[strlen(s)-1]!='#')
{
printf("请输入以‘#’结尾的表达式!\n");
printf("*----------------------------请继续操作--------------------------*\n\n");
break;
}
s1.top = -1;
s2.top = -1;
double num; //最终结果
num = getAnswer(s);
printf("该算术表达式结果为:\n%.3lf\n", num);
printf("*----------------------------请继续操作--------------------------*\n");
printf("\n");
break;
}
case 2:
{
printf("请输入关系表达式(其中 >= 用X表示, <= 用Y表示, == 用Z表示, != 用W表示,以'#'表示结束):\n");
scanf("%s", s);
if(s[strlen(s)-1]!='#')
{
printf("请输入以‘#’结尾的表达式!\n");
printf("*----------------------------请继续操作--------------------------*\n\n");
break;
}
double num = getAnswer(s);
int flag = (int)num;
if (flag == 1)
{
printf("该关系表达式结果为:\nTURE\n");
}
else
{
printf("该关系表达式结果为:\nFALSE\n");
}
printf("*----------------------------请继续操作--------------------------*\n");
printf("\n");
break;
}
case 3:
{
printf("请输入逻辑表达式( && 用'&'表示, || 用'|'表示,以'#'表示结束):\n");
scanf("%s", s);
if(s[strlen(s)-1]!='#')
{
printf("请输入以‘#’结尾的表达式!\n");
printf("*----------------------------请继续操作--------------------------*\n\n");
break;
}
double num = getAnswer(s);
int flag = (int)num;
if (flag == 1)
{
printf("该逻辑表达式结果为:\nTRUE\n");
}
else
printf("该逻辑表达式结果为:\nFALSE\n");
printf("*----------------------------请继续操作--------------------------*\n");
printf("\n");
break;
}
case 0:
{
printf("*----------------------------------------------------------------*\n");
printf("* *\n");
printf("* 程序结束,谢谢使用! *\n");
printf("* *\n");
printf("*----------------------------------------------------------------*\n");
return 0;
}
}
}
return 0;
}