加减运算 “中缀表达式”转“后缀表达式

我们将a + b 或 a - b这种形式称为中缀表达式,将a b + 或 a b -称为a + b 或 a - b 所对应的后缀表达式。

a + b - c 的后缀表达式为 a b + c -,转换过程如下:

步骤一:(a b +) - c				# 先转换 a + b => a b + 并将a b + 当成一个整体
步骤二:(a b +) c -				# 转换 (a b +) - c => (a b +) c -
步骤三:a b + c -				# 去掉括号就是最终的后缀表达式结果

本文将介绍,如何将一个加减运算表达式的中缀表达式形式转换为后缀表达式,并通过程序实现该算法。

文章开头已经演示了中缀表达式转换成后缀表达式的规则,现在需要使用抽象的表达式来描述这个规则,方便后面程序实现。

先考虑一个简单的表达式结构:a + b + c,可以用下面表达式表示:

expr -> expr + term
term -> a | b | c

然而上述表达式无法描述一个确定一个表达式,因为该表达式是无限递归的,如下图所示:

            expr + term
             / \     \
        expr + term + term
         / \      \     \
    expr + term + term + term
	/		 \      \      \
.... + term + term + term + term

修改表达式,消除无限递归

A -> Aα | β

A = expr, α = + term, β = term

上面表达式,A被Aα递归替换,直到A被β替换,表达式变成了βααα…α的形式,而且表达式是向左递归,而当分析一个表达式的时候希望是从左往右,因此需要使表达式向右递归,并且不是无限递归的。

A -> βR
R -> αR | e

e表示一个空的表达式

上面的表达式将递归放到右边,并且引入e来结束无限递归,当R为e时表达式变成了βααα…α的形式,因此,一个向左递归的表达式可以等价转换成向右递归的表达式即:

A -> Aα | β		<=>		A -> βR
						R -> αR | e

对于a + b - c这样的的表达式的抽象表达式为:

A -> Aα | Aβ | γ

对应的向右递归表达式为:

A -> γR
R -> αR | βR | e

A = expr

α = + term

β = - term

γ = term

expr -> term rest
rest -> + term rest
	  |	- term rest
      | e
term -> 0
	  | 1
	   ...
	  | 9

如上所示表达式可以描述任意一个加减运算表达式,严格讲每个加减分量只能是0~9之间的任意一位数,本文主要讲述后缀表达式的转换方法,所以这里只考虑最简单的情况。

加减中缀表达式转换后缀表达式实际上是符号位置的变化,数值顺序并未发生变化,因此只需要关心符号位置即可,实际上以最简单的情况来分析,就能得到一个规律,如下:

a + b => a b +
a - b => a b -
a + b -c => a b + c -
a + b -c + d => a b + c - d +
  1. 数字顺序相同

  2. 加减号总是在右边数字的后面

将一个中缀表达式串从左向右进行遍历,每次遇到数值就记录,遇到运算符时,在紧接着后面的数值之后记录该运算符,这样遍历完之后便得到一个后缀表达式。

对上面表达式修改如下:

expr -> term rest
rest -> + term {printf("+")} rest
	  |	- term {printf("-")} rest
      | e
term -> 0
	  | 1 {printf("1")}
	   ...
	  | 9 {printf("9")}

对一个表达式如:9 - 5 + 2 从左向右每个字符都套用上面的递归算法就可以得到这个表达式的后缀形式。

将上面表达式以代码的形式表示,下面使用C++语言来编写该算法:

#include <iostream>
#include <stdio.h>
class Parser
{
public:
	Parser(const std::string& expr)
		: _expr(expr)
		, _index(0)
		, _headChar(0)
	{
		if (expr.size() > 0)
		{
			_headChar = expr[_index];
		}
		else
		{
			_headChar = ' ';
		}
	}

public:
	// 定义一个函数只接受数字字符
	void term()
	{
		if (_headChar >= '0' && _headChar <= '9')
		{
			printf("%c", _headChar);
			match(_headChar);
		}
		else
		{
			printf("error syntax.%c\n", _headChar);
		}
	}

	// 获取下一个字符
	void match(char c)
	{
		if (c == _headChar)
		{
			if (_index == _expr.size() - 1)
			{
				_headChar = ' ';
			}
			else
			{
				while ((_headChar = _expr[++_index]) == ' ');
			}
		}
		else
		{
			printf("语法错误。\n");
		}
	}

	// 递归调用
	void rest()
	{
		if (_headChar == '+')
		{
			match('+'); term(); printf("+"); rest();
		}
		else if (_headChar == '-')
		{
			match('-'); term(); printf("-"); rest();
		}
		else
		{
			return;
		}
	}

	// 后缀表达式结果
	void expr()
	{
		term(); rest();
	}

private:
	int _index;
	char _headChar;
	string _expr;
};

void main()
{
	Parser parser("9 - 5 + 2"); 
	parser.expr();
	printf("\n");
}

上面代码定义了一个类Parser用来解析中缀表达式,并输出转换之后的结果。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值