设计并实现一个整型算术表达式计算器(C++)
题目要求是: 处理整型算术运算表达式,即运算符至少包含±*/()
上个学期学习了数据结构这门课程,着实被课后作业给整惨了。
在自己做作业时也切实体会到了修改BUG的不易,以及在网上找不到适合的源码的困难。所以我决定自己写。
并且现在是暑假也闲来无事,决定写一篇博客,来分享一下自己的代码,同时也希望我们一起学习一起进步。
******这个作业应该是我这门课第一个有难度的作业。首先实现整形计算器是需要数据结构栈的。我没有用C++STL库中自带的stack,而是使用的我们老师上课所讲的栈(后面的作恶也有让体会使用stl,具体函数功能其实都差不多)。注意,这里需要两种类型的栈,一个存放数据,一个存放运算符。所以我将栈定义为了模板类。
先简单说一下我们自己写的栈结构吧。我们有一个枚举类型errorcode,这个不用太在意,就是用来判断函数是否成功调用,返回调用的结果。栈的具体功能有判断空,取栈顶,入栈出栈等。由于使用的是链栈结构,所以判断满没什么实际意义。比较需要注意的就是取栈顶元素时,是通过函数get_top(T& x)形参引用类型传入的,即调用完这个函数之后,传入的实参值会改变为我们的栈顶元素的值,也就达到了取栈顶的目的。至于其他函数我自己觉得还是非常好理解的。
然后我们说我们的计算器吧。对于计算器的输入,我使用了string类型,用来存储初始输入的表达式,当表达式字符入栈时,因为数字字符输入的是对应ASCII码,需要将输入的数字字符,通过对应的ASCII码转化成对应的十进制数字。为此我定一个change()函数实现这一操作。而且,有可能输入的是多位的数字,所以需要判断一下输入的是几位,这个功能在main函数中实现的。
对于字符串,需要从左向右一个一个读,对于数字字符和运算符需要进入不同的栈,为了判断每个字符需要进入哪个栈,我也定义了一个isnumber()函数用来判断当前读取的字符是数字还是运算符。
如果当前需要判断的字符是运算符,那么我们还需要判断一下,该运算符是入栈还是进行栈顶运算符的计算,只有当当前运算符的优先级比栈顶运算符的优先级高,或者当前为左括号时,才会进行入栈操作。当当前运算符为右括号且栈顶为左括号时,左括号出栈且不进行运算,直接进入下一个字符的判断。只有当当前运算符为#且栈顶运算符也为#时,此时所有运算符均进行完了运算,那么运算结束,数据栈的栈顶即为运算结果。
上一个段落我们分析了种种情况。为此我设置了两个函数,一个priority()函数,用来判断运算符的优先级。一个jisuan()函数用来判断当前需要进行何种运算,并完成对数据栈的操作。
在主函数中,我设置了一个很大的for循环,用来逐个对输入的表达式进行扫描,并在循环体嵌套了许多的if else语句用来判断针对当前运算符应该进行何种情况的操作。比如,当前如果是运算符的话,如何判断他与当前栈顶运算符的优先级,判断完成后,若当前的较高,则会进行运算,并且当前栈顶运算符会出栈。然后在进行与下一个栈顶运算符的比较。因为这个我专门设置了一个while循环,直到当前运算符入栈或者为右括号时与左括号对应的操作,或者为#号时与栈顶#号的操作。
还有一些关于多位数数字的输入问题,因为对于用string类型的数据来说,输入一个多位数字表示多个字符,如果直接判断该数字的最高位直接入栈会出现错误,因此我也在判断其是否为数字字符后边使用了while循环,用来将该多位数字送去数据栈,详细可见我的代码。
这样,根据上述完成后,最后数据栈栈顶即为最后的运算结果。
啰嗦完之后,就直接上代码了,可以直接编译运行,输入表达式即可。
栈以及栈中每一个数据结构的模板
#ifndef STACK
#define STACK
enum errorcode {
success, underflow, overflow
};
template<class T> //结构体模板,因为要使用具有不同数据类型的结构体,所以使用了模板类
struct node {
T data;
node<T>* next;
};
template<class T>
class stack {
public:
stack();
~stack();
bool empty() const;
bool full() const; //基本无意义
errorcode get_top(T& x) const;
errorcode push(const T x);
errorcode pop();
private:
int count;
node<T>* top;
};
template<class T>
stack<T>::stack() {
count = 0;
top = NULL;
}
template<class T>
bool stack<T>::empty() const {
return count == 0;
}
template<class T>
errorcode stack<T>::get_top(T& x)const {
if (empty())
return underflow;
x = top->data;
return success;
}
template<class T>
errorcode stack<T>::push(const T x) {
node<T>* s = new node<T>;
s->data = x;
s->next = top;
top = s;
count++;
return success;
}
template<class T>
errorcode stack<T>::pop() {
if (empty()) return underflow;
node<T>* u;
u = top;
top = top->next;
delete u;
count--;
return success;
}
template<class T>
stack<T>::~stack() {
while (!empty())
pop();
}
#endif // !stack
main函数
#include<iostream>
#include<string>
#include"stack模板.h"
using namespace std;
int priority(char x);//判断运算符优先级
int change(char x);//数字ASCII码转数字
bool isnumber(char x);//判断输入的是否是数字
int jisuan(char x, stack<int>& num1);//判断符号运算属性,并运算返回结果
int main()
{
stack<int> num; //存储数据
stack<char> oper; //存储符号
int p, i; //p用来取运算完后数据栈的栈顶,既最后的运算结果
string s;
cout << "请输入一个算数表达式:";
cin >> s;
s += '#';
oper.push('#'); //给字符栈一个初始的值#
for (i = 0; i < s.size(); i++)
{
if (isnumber(s[i])) //判断当数字为多位时适当改变函数
{
int j = 0, t = 1, sum = 0, p;
while (isnumber(s[i + j])) //j记录是几位的数字
j++;
p = j; //保留下j
while (j > 0) //sum求出这个数字
{
sum += change(s[i + j - 1]) * t;
t *= 10;
j--;
}
num.push(sum); //sum入栈
i = i + p - 1; //i的值跳转
}
else
{
int k = 1;
char x, y; //x取当前字符栈栈顶元素
oper.get_top(x);
while (k)
{
if (priority(s[i]) > priority(x) || s[i] == '(') //当前字符优先级如果大于栈顶则入栈
{
oper.push(s[i]);
break;
}
else //如果不是则使用计算函数,并将结果送入数据栈
{
if (priority(s[i]) == priority(x) && priority(s[i]) == 0 || priority(s[i]) == priority(x) && priority(s[i]) == -1) //既判断左右括号见面,如果当前是右括号,而字符栈中是左括号,则不计算,且左括号出栈且进入下个字符判断
{
oper.pop();
break;
}
else
{
num.push(jisuan(x, num)); //计算结果,并将结果送去数据站
oper.pop(); //当前栈顶元素出栈
oper.get_top(y); //字符栈栈顶出栈后再判断当前字符与当前栈顶的优先级,将y的值给x,进入while循环
x = y;
}
}
}
}
}
num.get_top(p); //数据栈当前栈顶就是最终运算结果
for (i = 0; i < s.size() - 1; i++)
cout << s[i];
cout << '=' << p << endl; //输出运算结果
return 0;
}
//判断运算符优先级
int priority(char x) {
if (x == '+' || x == '-')
return 1;
else if (x == '*' || x == '/')
return 2;
else if (x == '(' || x == ')')
return 0;
else
return -1;
}
//数字ASCII码转数字
int change(char x)
{
int y;
y = x - 48;
return y;
}
//判断输入的是否是数字
bool isnumber(char x)
{
if (x >= '1' && x <= '9')
return 1;
else
return 0;
}
//判断符号运算属性,并运算返回结果
int jisuan(char x, stack<int>& num1)
{
int a, b, c;
if (x == '+')
{
num1.get_top(a);
num1.pop();
num1.get_top(b);
num1.pop();
c = a + b;
}
else if (x == '-')
{
num1.get_top(a);
num1.pop();
num1.get_top(b);
num1.pop();
c = b - a;
}
else if (x == '*')
{
num1.get_top(a);
num1.pop();
num1.get_top(b);
num1.pop();
c = a * b;
}
else if (x == '/')
{
num1.get_top(a);
num1.pop();
num1.get_top(b);
num1.pop();
c = b / a;
}
return c;
}