后缀表达式(4)-——中缀表达式到后缀表达式的转换,递归实现

本篇是介绍的是后缀表达式最难,也是最基本的一个问题。即如何将一个中缀表达式转化为后缀表达式。在网上可以找到许多非递归实现的示例代码,它们比本节介绍的代码要简洁且高效,可是不利于理解。本节舍近求远,介绍后缀表达式的递归实现方式,为的是把变换的中心思想解释清楚。


问题描述


输入: 是一串字符串,它表示的是包含加减乘除四则运算以及括号运算的中缀表达式。为了简单起见,我们假设表达式中的运算元都是0到9之间的正整数。这样可以规避词法分析的细节。i


输出 的是一个Element对象组成的数组,表示一个后缀表达式。


主要思想


我们将运算根据优先级分成三组


  • 加法减法运算。它是优先级最低的运算。我们用expr()函数处理。这里expr()函数需要考虑连续加减的情形。
  • 乘法除法运算。它是优先级较高的运算。我们用term()函数处理。这里term()函数需要考虑连续乘除的情形。
  • 括号运算。这是优先级最高的运算。我们在处理的时候,可以把括号内包含表达式看做是一个特殊的运算元,用factor函数处理。factor()函数除了处理括号运算外,也处理由单个数字组成的简单的运算元。


具体实现


expr方法,代码如下

void expr(char* s, int & sn, Element* eles, int& en)
{
	term(s, sn, eles, en);
	while(s[sn] == '+' || s[sn] == '-')
	{
		int op = s[sn++];
		term(s, sn, eles, en);
		eles[en].is_op = true;
		eles[en++].val = op;
	}
}


expr方法共有4个参数。s保存的是输入的中缀表达式,它是一个字符串。sn表征最近将要处理的字符的索引。eles数组用来保存最后生成的后缀表达式,而en表示目前已经生成的Element元素个数。


代码的第一行首先调用term方法。这里term()函数的作用是处理最外层非加法和减法运算的表达式。可分为如下三种情况考虑。

  • 如果表达式s中最外层不是加法或者减法运算。那么term()函数会直接把整个表达式处理完成。第4-10行的while循环将不会执行。
  • 如果表达式s可以写成a+b的形式,其中表达式a和b最外层都不是加法或者减法运算。那么第3行的term()函数会首先将表达式a处理完毕。当第3行执行结束后,sn会指向加号‘+’,因此会进入循环。进入循环后,代码第6行首先将加号保存,同时sn++后指向表达式b的第一个字符。这样在代码第7行调用的term()方法会把表达式b处理完毕。当表达式a和b都处理完毕后,我们最后才能把预先保存的加号‘+’运算保存到Element数组末尾。
  • 如果表达式s可以写成a+b-c的形式,其中表达式a,b和c最外层都不是加法或者减法运算。当第3行执行结束后,sn会指向加号‘+’,因此会进入循环。所不同的是,这一次循环会执行两次。具体的细节不再赘述。

term()函数定义与expr()函数完全类似。

void term(char* s, int & sn, Element* eles, int& en)
{
	factor(s, sn, eles, en);
	while(s[sn] == '*' || s[sn] == '/')
	{
		int op = s[sn++];
		factor(s, sn, eles, en);
		eles[en].is_op = true;
		eles[en++].val = op;
	}
}

factor()函数需要处理两种情况。

1. 简单的数字;2. 带括号的表达式。

因此在factor()函数的定义中,也分为这两者情况讨论。

void factor(char* s, int & sn, Element* eles, int& en)
{
	int op = s[sn++];
	if(op == '(')
	{
		expr(s, sn, eles, en);
		sn++;
	}
	else
	{
		eles[en].is_op = false;
		eles[en++].val = op - '0';
	}
}
代码第4-8行处理的是带括号的情形,而代码的第9-13行处理的是普通数字的情形。注意代码第7行,sn++的目的是把右圆括号pass掉。当然,我们这样处理,其实是假设用户输入的中缀表达式总是正确的。


最后附上完整的可执行代码。

#include 

typedef struct str_ele{
    bool is_op;
    int val;
} Element;

void factor(char* s, int & sn, Element* eles, int& en);
void term(char* s, int & sn, Element* eles, int& en);
void expr(char* s, int & sn, Element* eles, int& en);

int resolve(Element* eles, int& end)
{
	int end_t = end--;
	if(eles[end_t].is_op)
	{
		int operand2 = resolve(eles, end);
		int operand1 = resolve(eles, end);
		switch(eles[end_t].val)
		{
		case '+': return operand1 + operand2;
		case '-': return operand1 - operand2;
		case '*': return operand1 * operand2;
		case '/': return operand1 / operand2;
		}
	}
	else
	{
		return eles[end_t].val;
	}
}

void factor(char* s, int & sn, Element* eles, int& en)
{
	int op = s[sn++];
	if(op == '(')
	{
		expr(s, sn, eles, en);
		sn++;
	}
	else
	{
		eles[en].is_op = false;
		eles[en++].val = op - '0';
	}
}

void term(char* s, int & sn, Element* eles, int& en)
{
	factor(s, sn, eles, en);
	while(s[sn] == '*' || s[sn] == '/')
	{
		int op = s[sn++];
		factor(s, sn, eles, en);
		eles[en].is_op = true;
		eles[en++].val = op;
	}
}

void expr(char* s, int & sn, Element* eles, int& en)
{
	term(s, sn, eles, en);
	while(s[sn] == '+' || s[sn] == '-')
	{
		int op = s[sn++];
		term(s, sn, eles, en);
		eles[en].is_op = true;
		eles[en++].val = op;
	}
}

void print_elements(Element* eles, int sn)
{
	for(int i = 0; i < sn; i++)
	{
		if(eles[i].is_op)putchar(eles[i].val);
		else putchar(eles[i].val + '0');
	}
	putchar('\n');
}

int main()
{
	char s[256];
	Element eles[256];
	int sn, en;

	while(scanf("%s", s) != EOF)
	{
		sn = en = 0;
		expr(s, sn, eles, en);
		print_elements(eles, en);
		printf("%d\n", resolve(eles, --en));
	}
	return 0;
}



你可能感兴趣的:(编译原理)