C++ 四则运算表达式求值算法
作者:韦延昭
Email: yls8420@163.com
26,April 26, 2008
引言:四则运算表达式包含操作数、操作符以及括号等。操作数
可以是任意实数,操作符包括加( + )、减( - )、乘( * )以及
除( / )等。本文将利用 C++标准模板库(STL)中的堆栈和队列
来编程实现计算任意实数的四则运算表达式的值。
问题:设计一个处理和计算四则表达式的类,该类有一个字符串
类型的成员变量用来表示初始的中缀表达式的字符串,例如中缀表达
式:
(2.99-4.32)*(90.8-78.66)+78.0/3.14
通过该类的成员函数将该表达式的值计算出来。
要求:操作数可以是任意实数、括号对数以及表达式长度没有限
制。
分析:由于操作数是任意的实数,所以必须将原始的中缀表达式
中的操作数、操作符以及括号分解出来,并以字符串的形式保存;然
后再将其转换为后缀表达式的顺序,为什么要转换为后缀表达式的形
式呢?原因是后缀表达式可以很容易地利用堆栈计算出表达式的值。
例如有如下的中缀表达式:
a+b-c
转换成后缀表达式为:
ab+c-
然后分别按从左到右放入栈中,如果碰到操作符就从栈中弹出两个操
作数进行运算,最后再将运算结果放入栈中,依次进行直到表达式结
束。如上述的后缀表达式先将 a 和 b 放入栈中,然后碰到操作符“+”,
则从栈中弹出 a 和 b 进行 a+b 的运算,并将其结果 d(假设为 d)
放入栈中,然后再将 c 放入栈中,最后是操作符“-”,所以再弹出 d
和 c 进行 d-c 运算,并将其结果再次放入栈中,此时表达式结束,则
栈中的元素值就是该表达式最后的运算结果。当然将原始的中缀表达
式转换为后缀表达式比较关键,要同时考虑操作符的优先级以及对有
括号的情况下的处理,相关内容会在算法具体实现中详细讨论。
实现过程:大体上可以分为三步进行:
一、将原始的中缀表达式中的操作数、操作符以及括号按顺序分解出
来,并以字符串的形式保存。
二、将分解的中缀表达式转换为后缀表达式的形式,即调整各项字符
串的顺序,并将括号处理掉。
三、计算后缀表达式的值。
设计类:为了体现 C++ Object-Oriented 的特点,我们将此算
法在类中实现。首先设计一个类 ExpressionType,其具体定义如下:
/****************************************************************************************
* ExpressionType.h
****************************************************************************************/
#include <string>
#include <stack>
#include <queue>
using namespace std;
class ExpressionType
{
public:
ExpressionType();
ExpressionType(string m_string);
void operator =(string m_string);
double Calculate();
private:
queue<string> DivideExpressionToItem();
stack<string> ChangeToSuffix();
bool IsWellForm();
int Size();
private:
string m_string;
};
类 ExpressionType 的定义包含了三个头文件<string>、<stack>
以及<queue>,这是标准模板库里现成的东西,直接拿来用就行了,
不必再自己写 栈和队列, 它们都 在 std 命名空间里 。下面对类
ExpressionType 的成员作介绍:
u m_string: 私有成员变量,字符串类型。主要用于存储表示原始中缀表达式
的字符串。
u 这里定义了两个构造函数,不带参数的默认 m_string 为空,带 string 类型
参数的将参数值赋予成员变量 m_string,此外还定义了赋值运算符“=”,
目的是将一个表达式字符串 赋予 成员变量 m_string 。因此 生 成一个
ExpressionType 的对象可以有如下两种形式:
ExpressionType expr(“(2.99-4.32)*(90.8-78.66)+78.0/3.14”);
或者
ExpressionType expr;
Expr = “(2.99-4.32)*(90.8-78.66)+78.0/3.14”;
u DivideExpressionToItem(): 私有成员函数,返回值是一个 string 类型的
队列。其功能是将原始的中缀表达式中的操作数、操作符以及括号按顺序以
字符串的形式分解出来,然后保存在一个队列中(队列的操作规则是先进先
出)。
u ChangeToSuffix(): 私有成员函数,返回值是一个 string 类型的栈。其功
能是将队列中表示原始表达式各项的字符串调整顺序,转换成后缀表达式的
顺序,并处理掉括号,然后保存在一个栈中(栈的操作规则是先进后出)。
u IsWellForm():私有成员函数,返回 bool 值。其功能是判断原始表达式中的
括号是否匹配,如果匹配返回 true,否则返回 false。
u Size():返回原始表达式所包含的字节数。
u Calculate(): 公有成员函数,返回 double 类型的值。其功能是计算转换后
的后缀表达式的值,即原始中缀表达式的值。
函数定义:
下面分别介绍 ExpressionType 类成员函数的定义,这些定义全部
放在 实现文 件 ExpressionType.cpp 中, 并 包含 头 文 件
“ExpressionType.h”。
1)构造函数。两个构造函数都比较简单,如下所示:
ExpressionType::ExpressionType()
{
m_string = "";
}
ExpressionType::ExpressionType(string m_string)
{
this->m_string = m_string;
}
2)定义赋值运算符“=”。将一个字符串赋予 m_string,如下所示:
void ExpressionType::operator =(string m_string)
{
this->m_string = m_string;
}
3)Size()函数。返回原始中缀表达式所包含的字节数,如下所示:
int ExpressionType::Size()
{
return m_string.size();
}
4)IsWellForm()函数。判断原始中缀表达式的括号是否匹配,可以
利用栈简单实现,即遇到“(”进栈,遇到“)”就从栈中弹出一个元
素,直到表达式结束。如果栈为空则表示括号匹配,否则不匹配。算
法实现如下:
bool ExpressionType::IsWellForm()
{
stack<char> stack;
int size = Size();
char ch;
for(int i = 0; i < size; i++)
{
ch = m_string.at(i);
switch(ch)
{
case '(':
stack.push(ch);