实验内容:算术表达式求值(算术计算器)
一、实验目的
表达式求值是实现程序设计语言的基本问题之一,也是栈的应用的一个典型例子。一个算术表达式是由操作数(operand)、运算符(operator)和界限符(delimiter)组成的。假设操作数是正整数,运算符只含加减乘除等四种二元运算符,界限符有左右括号和表达式起始、结束符“#”,如:#(7+15)*(23-28/4)#。引入表达式起始、结束符是为了方便。设计一个程序,演示算术表达式求值的过程。
二、实验要求及实验环境
实验要求:
- 从文本文件输入任意一个语法正确的(中缀)表达式,显示并保存该表达式。
- 利用栈结构,把(中缀)表达式转换成后缀表达式,并以适当的方式展示栈的状态变化过程和所得到的后缀表达式。
- 利用栈结构,对后缀表达式进行求值,并以适当的方式展示栈的状态变化过程和最终结果。
实验环境:Windows11操作系统+VS Code编译器
三、设计思想
此实验主要分为栈结构的实现以及中缀表达式的处理两个部分
1.栈结构的实现
实验采用顺序栈的结构,利用template方式定义了一个栈的模板类,通过数组data[]储存栈中元素,整数型变量Top作为栈顶指针。
此部分主要实现了以下功能:
序号 | 函数名 | 函数功能 | 函数参数 | 函数返回值 |
1 | Stack() | 初始化栈 | 无 | 无 |
2 | push(n) | 压栈 | 进栈元素n | 无 |
3 | pop() | 出栈 | 无 | 栈顶元素d |
4 | size() | 查看栈长度 | 无 | 栈长度length |
5 | isEmpty() | 判断是否栈空 | 无 | bool变量x |
6 | top() | 读取栈顶元素 | 无 | 栈顶元素d |
7 | show() | 打印当前栈中元素 | 无 | 无 |
2.中缀表达式的处理
此部分函数调用已经逻辑关系如下:
- 首先是对中缀表达式的储存表示。
此过程通过一个字符数组x[]即可实现,
- 其次是中缀表达式到后缀表达式的转换
这里主要是对运算符“(”“+”“-”“*”“/”“)”的优先级判断。基本算法思想是:对上述字符数组从头至尾扫描,
- 遇见数字字符或小数点“.”直接存进后缀表达式的字符数组y[]中;
- 如遇到运算符,则判断其与栈顶元素的优先级后,执行对应操作后再进栈。从栈中弹出的运算符依序存进后缀表达式的字符数组y[]。
注意,在此步骤中,由于字符一次扫描一位,为方便可视化以及多位数的计算,在相邻两个字符间以空格间隔,表示多位数的连续数字字符之间则无空格隔开。
优先级如下表所示:
运算符 | 优先级及相关操作 |
“(” | 无序比较,直接进入栈 |
“*”或“/” | 当栈顶为“*”或“/”时,弹出当前栈顶直至不为“*”或“/”后进栈 |
“+”或“-” | 当栈顶不为“#”或“(”时,弹出当前栈顶直至为“#”或“(”后进栈 |
“)” | 无需进栈,逐个将栈顶弹出直至“(”恰好弹出栈 |
“#” | 第一次直接进栈,第二次直接将栈中元素全部弹出。 |
- 最后是对后缀表达式的求值
基本思想是从头至尾扫描后缀表达式y[],
- 遇到数字字符时通过ASCII码将其转换为float数据类型后压入栈中,
- 遇到运算符“+”“-”“*”“/”时,依次弹出栈顶的两个数字进行运算,运算结果压入栈中,继续扫描。
四、测试结果
测试用例:#(7.55+15)*((21-28/3)+3)#,#(2*(3+1)*(4.55+7.3))#
测试结果如下:
五、附录:源代码(带注释)
1. seqstack.h(手动实现顺序栈结构)
#include<iostream>
#define SIZE 50
using namespace std;
template<class T>class Stack
{
private:
T data[SIZE]; //顺序储存数据
int Top; //栈顶下标
public:
void stack() //栈的初始化
{
Top = -1;
}
void push(T n) //压栈
{
data[Top + 1] = n;
Top++;
}
T pop() //出栈
{
if(Top <= -1)
abort();
T d = data[Top];
Top--;
return d;
}
int size() //查看栈长度
{
return Top + 1;
}
T top() //读取栈顶元素
{
return data[Top];
}
bool isEmpty() //判断是否栈空
{
if(Top == -1)
return true;
else
return false;
}
void show() //打印当前栈中元素
{
for(int i = 0; i<=Top; i++)
cout<<data[i]<<" ";
if(Top < 0)
cout<<"栈空";
}
};
2. main.cpp(具体功能实现)
#include <iostream>
#include <fstream>
#include "seqstack.h"
using namespace std;
//中缀转后缀
void translate(char x[], char y[]);
//后缀的计算
float calculate(char y[]);
//四则运算
float operate(float x, float y, char c)
{
switch (c)
{
case '*': return (x * y); break;
case '/': return (y / x); break;
case '+': return (x + y); break;
case '-': return (y - x); break;
default: break;
}
}
int main()
{
char x[SIZE],y[SIZE];
ifstream infile;
int i = 0;
//文件读取
infile.open("data.txt");
while(!infile.eof()) //判断文件是否读取完毕
{
i++;
infile.getline(x,SIZE);
cout<<"============================================="<<endl;
cout<<"读取的第"<<i<<"个中缀表达式为: "<<x<<endl;
cout<<"============================================="<<endl;
cout<<"转换过程中栈的变化过程为:"<<endl;
translate(x,y);
cout<<"============================================="<<endl;
cout<<"后缀表达式为: "<<y<<endl;
cout<<"============================================="<<endl;
cout<<"计算过程为: "<<endl;
float m = calculate(y);
cout<<"============================================="<<endl;
cout<<"最后中表达式结果为: "<<m<<endl;
cout<<"============================================="<<endl;
}
infile.close();
}
void translate(char x[], char y[])
{
Stack<char> s; //定义运算符栈
s.stack();
int i = 0, j = 0,number = 0;
//初始化字符数组
for(int k = 0; k < SIZE; k++)
y[k] = ' ';
//逐个扫描
while (x[i] != '\0')
{
//数字字符
if (x[i] >= '0' && x[i] <= '9')
{
while((x[i + 1] >= '0' && x[i + 1] <= '9' ) || (x[i + 1] == '.'))
{
y[j] = x[i];
i++;j++;
}
y[j] = x[i]; y[j + 1] = ' ';
j += 2;
}
//运算符处理
if(x[i] == '#')
{
number++;
if(number == 1)
s.push(x[i]);
//当第二次遇到#时结束转换
if(number == 2)
{
while(s.top() != '#')
{
char x = s.pop();
y[j] = x;
y[j + 1] = ' ';
j += 2;
}
s.show();cout<<'\n';
char x = s.pop();
s.show();cout<<'\n';
return;
}
}
if (x[i] == '(' )
s.push(x[i]);
if (x[i] == '*' || x[i] == '/')
{
while(s.top() == '*' || s.top() == '/')
{
char x = s.pop();
y[j] = x;
y[j + 1] = ' ';
j += 2;
}
s.push(x[i]);
}
if (x[i] == '+' || x[i] == '-')
{
while(s.top() != '#' && s.top() != '(')
{
char x = s.pop();
y[j] = x;
y[j + 1] = ' ';
j += 2;
}
s.push(x[i]);
}
if(x[i] == ')')
{
while(s.top() != '(')
{
char x = s.pop();
y[j] = x;
y[j + 1] = ' ';
j += 2;
}
char q = s.pop();
}
i++;
//展示当前栈状态
s.show();
cout<<'\n';
}
// 加入字符串的结尾
y[j+1] = '\0';
}
float calculate(char y[])
{
Stack<float> numbers;
numbers.stack();
int i = 0;
while(y[i] != '\0')
{
//数字字符处理
if(y[i] >= '0' && y[i] <= '9')
{
float m = 0;
//整数部分
while(y[i + 1] >= '0' && y[i + 1] <= '9')
{
m += (y[i] - '0');
m *= 10;
i++;
}
m += (y[i] - '0');
//小数部分
if(y[i + 1] == '.')
{
i = i + 2;
float j = 1;
while(y[i + 1] >= '0' && y[i + 1] <= '9')
{
m += ((float)(y[i] - '0')/(10*j));
j *= 10;
i++;
}
m += ((y[i] - '0')/(10*j));
}
numbers.push(m);
}
//运算符处理
if(y[i] == '*' || y[i] == '/' || y[i] == '+' || y[i] == '-' )
{
numbers.show();
cout<<y[i]<<endl;
float x1,x2;
x1 = numbers.pop();
x2 = numbers.pop();
float x = operate(x1,x2,y[i]);
//cout<<x<<endl;
numbers.push(x);
}
i++;
}
return numbers.pop();
}