【问题描述】
表达式计算是实现程序设计语言的基本问题之一,也是栈的应用的一个典型例子。设计一个程序,演示用算符优先法对算术表达式求值的过程。
【实现要求】
(1) 以字符序列的形式从终端输入语法正确的、不含变量的整数表达式。利用下表给出的算符优先关系,实现对算术混合运算表达式的求值,并仿照求值中运算符栈、运算数栈、输入字符和主要操作的变化过程。
(2) 扩充运算符集,如增加乘方、单目减、赋值等运算。
(3) 计算器的功能和仿真界面(选作)。
【测试数据】
下列表达式:
3*(7-2); 8; 1+2+3+4; 88-15; 1024/48; 1024/(48); (20+2)(6/2);
3-3-3; 8/(9-9); 2*(6+2*(3+6*(6+6))); (((6+6)*6+3)*2+6)*2;
【实现提示】
(1) 设置运算符栈和运算数栈辅助分析算符优先关系。
(2) 在读入表达式的字符序列的同时,完成运算符和运算数(整数)的识别处理,以及相应的运算。
(3) 在识别出运算数的同时,要将其字符序列形式转换成整数形式。
(4) 在程序的适当位置输出运算符栈、运算数栈、输入字符和主要操作的内容。
实现思路:
主要使用的是栈的结构。程序实现一共分为两步:第一步,将输入的表达式转化为计算机方便处理的后缀表达式,;第二步,计算后缀表达式。
其中第一步,使用了两个栈:操作符存储栈(op)以及后缀表达式存储栈(postexp)。而第二步,则是通过一个辅助栈,结合后缀表达式存储栈(postexp)即主栈,分别对每个操作符以及对应得操作数进行计算,计算完成后再将其压入主栈,最后当整个表达式计算完成后再将最后结果弹出,并输出。
在处理单目操作符:“++”与“–”将其转为“a”与“s”方便进行处理。
操作:
直接输入表达式,再输入“enter”即可得到表达式的结果,会忽略空格。
main.cpp
#include<iostream>
#include<string>
#include<stdlib.h>
#include"stack.h"
#include<Windows.h>
using namespace std;
char Precede(char a, char b) {
int i, j;
char pre[][10] = {
/*运算符之间的优先级制作成一张表格*/
{ '>','>','<','<','<','>','<','<','<','>' },
{ '>','>','<','<','<','>','<','<','<','>' },
{ '>','>','>','>','<','>','<','<','<','>' },
{ '>','>','>','>','<','>','<','<','<','>' },
{ '<','<','<','<','<','>','<','<','<','0' },
{ '>','>','>','>','0','=','>','>','>','>' },
{ '>','>','>','>','<','>','>','<','<','>' },
{ '>','>','>','>','<','>','>','>','>','>' },
{ '>','>','>','>','<','>','>','>','>','>' },
{ '<','<','<','<','<','0','<','<','<','=' } };
switch (a) {
case '+': i = 0; break;
case '-': i = 1; break;
case '*': i = 2; break;
case '/': i = 3; break;
case '(': i = 4; break;
case ')': i = 5; break;
case '^': i = 6; break;
case 'a': i = 7; break;//用a代替++符号方便处理
case 's': i = 8; break;//用s代替--符号方便处理
case '#': i = 9; break;
}
switch (b) {
case '+': j = 0; break;
case '-': j = 1; break;
case '*': j = 2; break;
case '/': j = 3; break;
case '(': j = 4; break;
case ')': j = 5; break;
case '^': j = 6; break;
case '++': j = 7; break;
case '--': j = 8; break;
case '#': j = 9; break;
}
return pre[i][j];
}
int tranNum(stackL *postexp, string exp, int i)//将中缀表达式中的多位数,按照从低位到高位顺序压入postexp栈中,方便后续计算
{
int len = 0,j;
char expRead = exp[i];
while (expRead <= '9'&&expRead >= '0')//获得数符长度
{
//PushL(postexp, expRead);
i++;
expRead = exp[i];
len++;
}
for (j = i-1; j >= i - len;j--)
{
PushL(postexp, exp[j]);
}
i--; //使i恰好下次循环指向非数字
PushL(postexp, '#');
return i;
}
void tranPost(stackL *op, stackL*postexp, string exp)//将中缀表达式转化为后缀表达式
{
int i;
char expRead, tempChar,preOp;
InitStackL(op);
InitStackL(postexp);
PushL(op, '#');
PushL(postexp, '#');
for (i = 0; i < (int)exp.length(); i++)
{
expRead = exp[i];
if (expRead == '#') //表达式尾端
break;
else if (expRead == ' ') //忽略空格
continue;
else if (expRead <= '9'&&expRead >= '0') //处理操作数,传入i,postexp,exp,返回i
{
i = tranNum(postexp, exp, i);
}
else if (expRead != '(' && expRead != ')' && expRead == exp[i + 1])//处理单目运算符(++、--)
{
if (expRead == '+')
PushL(op, 'a');
else if (expRead == '-')
PushL(op, 's');
else
{
cout << "error\n";
break;
}
i++; //使遍历跳过自增自减符号到下一字符
}
else //处理普通操作符
{
tempChar = GetTopL(op);
preOp = Precede(tempChar, expRead);
if (expRead == ')')
{
tempChar = PopL(op);
while (tempChar != '(')
{
PushL(postexp, tempChar);
//PushL(postexp, '#');
tempChar = PopL(op);
}
}
else if (preOp == '>')
{
PushL(postexp, PopL(op));
PushL(op, expRead);
//PushL(postexp, '#');
}
else if (preOp == '<')
PushL(op, expRead);
}
}
while (op->next->data != '#')
{
tempChar = PopL(op);
PushL(postexp, tempChar);
}
}
int ary(int expn)
{
int i;
int sum = 1;
for (i = 0; i < expn; i++)
{
sum *= 10;
}
return sum;
}
int getValue(stackL *assistStack)//求得属于同一数值的连续数符的值
{
int value = 0;
int ex = 0;
char charValue;
charValue = PopL(assistStack);
while (charValue != '#')
{
value += (charValue - 48)*ary(ex);
ex++;
charValue = PopL(assistStack);
}
return value;
}
void pushBack(stackL *postexp, int valueBack)//将计算出来的int值化作字符,并从低位到高位压入主栈中
{
int i = 0;
char valueChar;
if (valueBack == 0)
PushL(postexp, '0');
while (valueBack != 0)
{
valueChar= (valueBack % 10)+48;
PushL(postexp, valueChar);
valueBack = valueBack / 10;
}
PushL(postexp, '#');
}
int power(int value, int expn)
{
int result = 1;
for (int i = 0; i < expn; i++)
{
result *= value;
}
return result;
}
int partCalu(stackL *postexp,stackL *assistStack )//计算小段式的值,此时两个操作数分别在两个栈中,操作数在辅助栈里
{
int value_1, value_2, value_3;
char opera;
PushL(assistStack, PopL(postexp));
while (GetTopL(postexp) <= '9'&&GetTopL(postexp) >= '0')//使两个操作数和运算符都在辅助栈中,方便操作数的还原
{
PushL(assistStack, PopL(postexp));
}
value_1 = getValue(assistStack);
value_2 = getValue(assistStack);
opera = PopL(assistStack);
switch (opera)
{
case '+':
value_3 = value_1 + value_2;
break;
case '-':
value_3 = value_1 - value_2;
break;
case '*':
value_3 = value_1 * value_2;
break;
case '/':
if (value_2 == 0)
{
cout << "error:除数为0" << endl;
return 0;
}
else
value_3 = value_1 / value_2;
break;
case'^':
value_3 = power(value_1, value_2);
default:
break;
}
if (assistStack->next == NULL && postexp->next == NULL)
{
cout << value_2;
return 0;
}
else if (assistStack->next == NULL && postexp->next->next == NULL)//结束条件
{
cout << value_3;
return 0;
}
pushBack(postexp, value_3);//将部分求值结果重新压入主栈
if (GetTopL(assistStack) <= '9'&&GetTopL(assistStack) >= '0')//再次满足部分求值条件
partCalu(postexp, assistStack);
}
int caluValue(stackL *postexp)
{
int control = 1;
int value = 0;
stackL *assistStack;
char postRead,opera;
assistStack = (stackL*)malloc(sizeof(stackL));
InitStackL(assistStack);
postRead = GetTopL(postexp);
while (postexp->next != NULL)
{
/*主栈栈顶为#时进行是否部分求值检测,
如果辅助栈顶为数符的话,则进行部分求值,求值后将值继续压入主栈,
压入时注意低位先压入,这样就可以在连续求值时进行递归*/
if (postRead == '#'&&assistStack->next!=NULL)
{
if (GetTopL(assistStack) <= '9'&&GetTopL(assistStack) >= '0')
{
control =partCalu(postexp, assistStack);
if (control == 0)
return 0;
}
else if(GetTopL(assistStack) > '9'|| GetTopL(assistStack) < '0')
PushL(assistStack, PopL(postexp));
}
else if (postRead == 'a'||postRead == 's')
{
opera=PopL(postexp);
if (GetTopL(postexp) != '#')
{
cout << "单目运算符运用错误,应放在数字后" << endl;
exit(0);
}
else
{
PushL(assistStack,PopL(postexp));
while (GetTopL(postexp) <= '9'&&GetTopL(postexp) >= '0')//将数符弹出主栈 并压入辅助栈中
{
PushL(assistStack, PopL(postexp));
}
if (opera == 'a')
pushBack(postexp, getValue(assistStack) + 1);
else if (opera == 's')
pushBack(postexp, getValue(assistStack) - 1);
}
}
else
PushL(assistStack, PopL(postexp));
postRead = GetTopL(postexp);
}
}
int main()
{
while (1)
{
string express;
cout << "please enter the expression statement,and end with '#'" << endl;
cin >> express;
stackL *op, *postexp;
op = (stackL*)malloc(sizeof(stackL)); // 操作符栈
postexp = (stackL*)malloc(sizeof(stackL)); //后缀表达式栈
tranPost(op, postexp, express); //将输入的中缀表达式转化为后缀表达式
cout << express << " = ";
caluValue(postexp); //计算后缀表达式的值
cout << "\n\n";
free(op);
free(postexp);
}
}
stack.h
#include<stdlib.h>
#include<iostream>
using namespace std;
typedef char Elemtype;
typedef struct LinkStack
{
Elemtype data;
struct LinkStack *next;
}stackL;
//初始化
void InitStackL(stackL *lst)
{
lst->next = NULL;
}
//判断是否为空,为空则返回1
int StackLEmpty(stackL *lst)
{
return (lst->next == NULL);
}
//压入
void PushL(stackL *lst, Elemtype x)
{
stackL *p;
p = (stackL*)malloc(sizeof(stackL));
p->next = lst->next;
p->data = x;
lst->next = p;
}
//弹出
Elemtype PopL(stackL* lst)
{
stackL *toFree;
if (lst->next == NULL);
//cout << "the stack is empty\n";
else
{
Elemtype x;
x = (lst->next)->data;
toFree = lst->next;
lst->next = (lst->next)->next;
free(toFree);
return x;
}
return 0;
}
//获取栈顶元素
Elemtype GetTopL(stackL* lst)
{
if (lst->next == NULL);
//cout << "the stack is empty\n";
else
{
Elemtype x;
x = lst->next->data;
return x;
}
return 0;
}