1 实验任务
编写一个中间代码生成程序,能将算术表达式等翻译成逆波兰、三元组或四元组形式(选择其中一种形式即可)。
2 实验内容
(1)实验要求
将算术表达式等翻译成逆波兰、三元组或四元组形式
(2)输入格式
包含各种算术表达式的文本文件。
(3)输出格式
程序能输出生成的逆波兰、三元组或四元组序列。
3 算法描述
人们常用的算术表达式是中序表达式,而逆波兰表达式是后续表达式,在计算机编程中,后续表达式更易于求得表达式结果。
此处实现的算法主要是对栈的应用,主要实现是先给+、-、*、/、(设定优先级,然后读入符号,若是数字则直接压入结果栈,若是左括号则压入符号栈,是右括号则弹结果栈直到遇到左括号。
其他情况则按优先级压入,若当前符号优先级大于栈顶,直接压入符号栈,否则弹出符号栈到结果栈,直到为空或者符号优先级大于栈顶再压入符号栈。
而逆波兰表达式的求值则较为简单,每次从结果栈弹出三个值,分别是两个操作数和一个操作符,然后进行运算后压栈,当栈内只剩一个元素时,那个元素往往就是运算结果。
对于出错处理部分,将它们分成了三个错误:
- 字符非法:通过遍历字符看起是否合法实现。
- 操作符连续:维护一个bool的变量记录上个字符是否代表操作符。
- 括号无法匹配:利用一个栈。若左括号就压栈,右括号时查看是否是左括号处于栈顶,是的话就弹栈,不是的话就出错。最后,如果这个栈有剩余的左括号,说明出错。
4 代码
主要包含四个函数:
- Convert:将输入的中序表达式转化为结果栈的形式(这里用链表模拟栈)。
- GetRes:以Convert的输出做输入,求得后序表达式的值。
- CheckInput:查看输入是否有错误。
- error:输出对应错误。
//将中序表达式转化为后序表达式
//并利用后序表达式计算结果
#include<iostream>
#include<list>
#include<cstdio>
#include<string.h>
#include<string>
#include<map>
using namespace std;
list<string> ope; //运算符栈
list<string> res; //结果栈
map<string,int> oper; //存储算符优先级
list<int> container; //运算栈
char invalidChar;
bool valid = true;
void error(int x){
valid = false;
switch(x){
case 1://)(()
cout<<"非法字符"<<invalidChar<<endl;
break;
case 2:
cout<<"连续的操作符"<<invalidChar<<endl;
break;
case 3:
cout<<"无法匹配括号"<<endl;
break;
}
}
void CheckInput(string s){
list<char> bracketStack;
bool lastOp = true;//记录上一个是否为运算符
for(int i = 0;i<s.length();i++){
if(s[i]>='0'&&s[i]<='9'){
lastOp = false;
}
else if(s[i]=='+'||s[i]=='-'||s[i]=='*'||s[i]=='/'){
if(lastOp){
invalidChar=s[i];
error(2);
}
lastOp=true;
}
else if(s[i]=='('){
bracketStack.push_back('(');
lastOp=true;//左括号后不能直接出现运算符
}
else if(s[i]==')'){
if(bracketStack.empty()||bracketStack.back()!='('){
//不匹配
error(3);
}
else{
bracketStack.pop_back();
}
}
else{
lastOp = false;
invalidChar=s[i];
error(1);
}
}
if(!bracketStack.empty())
error(3);
}
string Convert(char s[])
{
oper["+"] = 1; //运算符优先级越低,oper越低
oper["-"] = 1;
oper["*"] = 2;
oper["/"] = 2;
oper["("] = 0;
int i = 0;
while(true)
{
if (i >= strlen(s))
break;
while (s[i]==' '||s[i]=='/t')//去除无效的空格
++i;
if(s[i]>='0'&&s[i]<='9') //若为数字则获取
{
string num;
while (s[i] >= '0'&&s[i] <= '9')
num.push_back(s[i++]);
res.push_back(num); //数字直接压入结果栈
continue;
}
else if(s[i]=='(')
ope.push_back("(");
else if(s[i]==')') //是右括号则匹配
{
while(true)
{
if(ope.back()=="(")
{
ope.pop_back();
break;
}
res.push_back(ope.back());
ope.pop_back();
}
}
else
{
string num;
num.push_back(s[i]);
while(!ope.empty())
{
if(oper[num]>oper[ope.back()])//当前符号优先级大于栈顶,直接压入符号栈
{
ope.push_back(num);
break;
}
res.push_back(ope.back());//否则弹出符号栈到结果栈,直到为空或者符号优先级大于栈顶再压入符号栈。
ope.pop_back();
}
if(ope.empty())
ope.push_back(num);
}
++i;
}
while(!ope.empty())//最后把符号栈的全部内容弹到结果栈
{
res.push_back(ope.back());
ope.pop_back();
}
string result = "";
while(!res.empty())//输出后序表达式
{
result+=res.front()+" ";
res.pop_front();
}
return result;
}
int GetRes(string s)
{
int res;
int i = 0;
while(true)
{
if (i >= s.length()-1)
{
break;
}
while (s[i]==' '||s[i]=='/t')//去除无效的空格
++i;
if(s[i]>='0'&&s[i]<='9')
{
int num=0;
while (s[i] >= '0'&&s[i] <= '9')
num = num*10 + s[i++]-'0';
container.push_back(num);
continue;
}
else
{
int b = container.back();
container.pop_back();
int a = container.back();
container.pop_back();
int c;
if(s[i]=='+')
c = a+b;
else if(s[i]=='-')
c = a-b;
else if(s[i]=='*')
c = a*b;
else if(s[i]=='/')
c = a/b;
container.push_back(c);
}
++i;
}
return container.back();
}
int main()
{
char s[30];
while (scanf("%s", &s) != EOF) {
ope.clear();
res.clear();
container.clear();
valid = true;
CheckInput(s);
if(!valid)
continue;
string ansStr = Convert(s); //把中序的算数表达式
cout<<ansStr<<endl; //输出逆波兰表达式
cout<<"算数结果:"<<GetRes(ansStr)<<endl; //输出算数结果
}
}
//56+8130/(2+67)*8
//9+1/1-7*2+123+456/123+(98+87)
//56+(8130/((2+67)-6*5+7)*8)
//56-+8130/(*2+67)*8
//~45+a/5
//5+2)(
5 运行结果
(1)测试一:56+8130/(2+67)8
(2)测试二:9+1/1-72+123+456/123+(98+87)
(3)测试三:56+(8130/((2+67)-6*5+7)*8)
(4)测试四:对三种出错情况进行测试。
56-+8130/(*2+67)*8触发错误:连续的操作符
~45+a/5触发错误:非法字符
5+2)(触发错误:括号不匹配