这几天学习编译原理,发现词法分析中需要用到这个中缀表达式转后缀,但当初学数据结构的时候就理解的不是很清楚,因此又学习了一下。
转化算法还是比较容易和清晰的,当然,前提是使用好的教材。
先给出转化算法的整体流程,然后进行代码实现。
转化步骤
建立一个存放操作符的空栈,以及一个存放后缀表达式的空字符串post
,然后遍历中缀表达式,根据当前不同的字符进行不同的操作:
- 若当前为数字,则直接放在
post
末尾 - 若当前为字符,记为
c
,然后将当前栈顶字符记为t
,根据c
与t
的优先级进行不同操作:- 若当前栈为空,则将
c
压入栈 - 若优先级
t
>c
,则弹出t
,放到post
末尾,然后c
继续与新栈顶比较 - 若优先级
t
<c
,则将c
压入栈 - 若优先级
t
=c
,注意此时t
必为'('
,c
必为')'
,弹出t
- 若当前栈为空,则将
在遍历结束后,将栈中剩余的字符都放入post
,至此,转化完成。
字符优先级表
上面提到了操作符的优先级,为了简化实现代码,我们先根据优先级构造优先级表,如下所示:
const char precedence_table_[9][9] = {
// + - * / % ^ ! ( )
{'>', '>', '<', '<', '<', '<', '<', '<', '>'}, // +
{'>', '>', '<', '<', '<', '<', '<', '<', '>'}, // -
{'>', '>', '>', '>', '>', '<', '<', '<', '>'}, // *
{'>', '>', '>', '>', '>', '<', '<', '<', '>'}, // /
{'>', '>', '>', '>', '>', '<', '<', '<', '>'}, // %
{'>', '>', '>', '>', '>', '>', '<', '<', '>'}, // ^
{'>', '>', '>', '>', '>', '>', '>', ' ', '>'}, // !
{'<', '<', '<', '<', '<', '<', '<', '<', '='}, // (
{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '} // )
}
其中行表示栈顶字符t
,列表示输入字符c
,举个例子,precedence_table_[1][3]
表示,当栈顶t
为-
,此时输入c
为/
时,结果为<
,即此时优先级t
< c
。
代码实现
API
先看一下整体结构与功能:
class Converter {
private:
const char operator_list_[9];
const int ol_size = 9;
const char precedence_table_[9][9];
string expression_;
int e_size_;
char compare(char a, char b) const;
int find(char c) const;
string read_digit(int& lo) const;
public:
void input(string expression);
string convert() const;
};
其中:
operator_list_
如下所示:
{'+', '-', '*', '/', '%', '^', '!', '(', ')'};
注意,operator_list_
中字符的顺序应该与字符优先级表中一致
compare(a,b)
用于比较字符的优先级find(c)
用于查找字符在opeartor_list_
中的下标read_digit(i)
用于读取数字,因为数字可能有多位以及小数点convert()
实现转化的算法
主要逻辑
convert()
中实现上面所说的算法:
string Converter::convert() const {
stack<char> s;
int i = 0;
string postfix = "";
while (i < e_size_) {
if (isdigit(expression_[i])) { // digit
// i is increased in read_digit
string digit = read_digit(i).append(" ");
postfix.append(digit);
}
else if (expression_[i] == ' ') i++; // skip ' '
else { // operator
if (s.empty()) s.push(expression_[i++]);
else {
char top = s.top(), input = expression_[i];
char precedence = compare(top, input);
if (precedence == '<') {
s.push(input); i++;
}
else if (precedence == '>') {
s.pop(); postfix.push_back(top);
}
else if (precedence == '=') {
s.pop(); i++;
}
}
}
}
// clean stack
while (!s.empty()) {
char top = s.top();
if (top == '(') {
cerr << "There is a '(' not matching with a ')'";
exit(0);
}
postfix.push_back(s.top());
s.pop();
}
return postfix;
}
辅助函数
input
用于读入中缀表达式:
void Converter::input(string expression) {
expression_ = expression;
e_size_ = expression_.length();
}
find
查找c
在operator_list_
中的下标,用于计算优先级。
int Converter::find(char c) const {
for (int i = 0; i < ol_size; i++) {
if (c == operator_list_[i]) return i;
}
return -1;
}
compare
比较两个字符的优先级:
char Converter::compare(char top, char input) const {
int i1 = find(top), i2 = find(input);
if (i2 == -1) { // i1 must be >= 0
string error = "The input contains illegal char: ";
error.push_back(input);
cerr << error << endl;
exit(0);
}
return precedence_table_[i1][i2];
}
read_digit
读取一个完整的数字,可能为浮点数以及多位数:
string Converter::read_digit(int& lo) const {
string digit = "";
bool dot = false;
for (; lo < e_size_; lo++) {
char c = expression_[lo];
if (isdigit(c))
digit.push_back(c);
else if (c == '.') {
if (!dot) {
digit.push_back(c);
dot = true;
}
else {
cerr << "The expression contains illegal char '.' in " + lo
<< endl;
exit(0);
}
}
else break;
}
return digit;
}