平常我们书写的表达式称为中缀表达式,因为它将运算符放在两个操作数中间,许多情况下为了确定运算顺序,括号是不可少的,而中缀表达式就不必用括号了。
后缀标记法:书写表达式时采用运算紧跟在两个操作数之后,从而实现了无括号处理和优先级处理,使计算机的处理规则简化为:从左到右顺序完成计算,并用结果取而代之。
例如:8–(3+2*6)/5+4可以写为:8 3 2 6*+5/–4+
其计算步骤为:8 3 2 6 * + 5 / – 4 +
8 3 12 + 5 / – 4 +
8 15 5 / – 4 +
8 3 – 4 +
5 4 +
9
编写一个程序,完成这个转换,要求输出的每一个数据间都留一个空格。
就一行,是一个后缀表达式。输入的符号中只有这些基本符号“0123456789+-*/^()”,并且不会出现形如2*-3的格式。
表达式中的基本数字也都是一位的,不会出现形如12形式的数字。
所输入的字符串不要判错。
若干个中缀表达式,第I+1行比第I行少一个运算符和一个操作数,最后一行只有一个数字,表示运算结果。
运算的结果可能为负数,“/”以整除运算。
8–(3+2*6)/5+4
8 3 2 6 * + 5 / – 4 +
8 3 12 + 5 / – 4 +
8 15 5 / – 4 +
8 3 – 4 +
5 4 +
9
分析:通过这个题目练习了编译原理的一些内容。包括递归下降算法,左递归的消除,语法制导翻译的少部分内容。
通过递归下降转换为后缀式,然后通过后缀式的计算方法来计算表达式,每次计算出一个结果将整个后缀式输出一次。
题目中要求的表达式消除左递归后为。
E->T1E1
E1->+T1E1 | -T1E1
T1->T2T11
T11->*T2T11 | /T2T11
T2->T3T22
T22->^T3T22
T3->(E) | i
然后编码就很容易了,代码如下。应为这个题目保证输入都正确,因此没进行出错的提示。
#include <iostream>
#include <list>
#include <string>
#include <cmath>
using namespace std;
string str; //句子
int i = 0; //读入字符的位置
list<char> postFix;
void T();
void E1();
void T1();
void T11();
void T2();
void T22();
void T3();
void G();
void F();
void S();
void E()
{
T1();
E1();
}
void E1()
{
if(i < str.size() && (str[i] == '+' || str[i] == '-'))
{
int ti = i;
i++;
T1();
if (str[ti] == '+')
{
postFix.push_back('+');
}
else
{
postFix.push_back('-');
}
E1();
}
}
void T1()
{
T2();
T11();
}
void T11()
{
if(i < str.size() && (str[i] == '*' || str[i] == '/'))
{
int ti = i;
i++;
T2();
if(str[ti] == '*')
{
postFix.push_back('*');
}
else if(str[ti] == '/')
{
postFix.push_back('/');
}
T11();
}
}
void T2()
{
T3();
T22();
}
void T22()
{
if (i < str.size() && str[i] == '^')
{
i++;
T3();
postFix.push_back('^');
T22();
}
}
void T3() // 终结符 或 ()
{
if(isalnum(str[i]))
{
postFix.push_back(str[i]);
i++;
}
else if(str[i] == '(')
{
i++;
E();
if(str[i] == ')')
{
i++;
}
}
}
void print(list<int> &operand)
{
list<int>::iterator it;
for (list<int>::iterator it = operand.begin(); it != operand.end(); ++it)
{
cout<<*it<<" ";
}
for (list<char>::iterator it = postFix.begin(); it != postFix.end(); ++it)
{
cout<<*it<<" ";
}
cout<<endl;
}
void cal() // 表达式是自左向右结合
{
list<int> operand;
print(operand);
for (;postFix.size() > 0;)
{
if (isalnum(postFix.front()))
{
operand.push_back(postFix.front() - '0');
postFix.pop_front();
}
else
{
char c = postFix.front();
postFix.pop_front();
int n1 = operand.back();
operand.pop_back();
int n2 = operand.back();
operand.pop_back();
switch(c)
{
case '-':
operand.push_back(n2-n1);
break;
case '+':
operand.push_back(n2+n1);
break;
case '*':
operand.push_back(n2*n1);
break;
case '/':
operand.push_back(n2/n1);
break;
case '^':
operand.push_back((int)pow(n2*1.0,n1*1.0));
break;
}
print(operand);
}
}
}
int main()
{
cin >> str;
E();
cal();
return 0;
}
一些参考资料:
[1]http://wenku.baidu.com/view/2ae7e28fcc22bcd126ff0c08.html
编译原理6-4.2-4.3- 自顶向下翻译-递归下降翻译
[2]http://blog.sina.com.cn/s/blog_687911280100nc8t.html
左递归消除。
[3]http://wenku.baidu.com/view/c3c9255f804d2b160b4ec0e7.html?re=view
递归下降翻译
[4]http://wenku.baidu.com/view/cb7d67631ed9ad51f01df254.html?re=view
第六章 属性文法和语法制导翻译
[5]http://blog.csdn.net/chosen0ne/article/category/1247484
该文将了四则运算优先级的定义