您的位置:首页 > 其它

MFC计算器 用vector实现和计算逆波兰表达式

2014-09-06 22:52 381 查看
短学期本来已逃脱计算器的厄运,因为一直纠结不清我计算器的架构,不知如何下手;但学了MFC的底层实现机制和框架后发现计算器还是比较好实现的,于是总结下做计算器的架构和关键算法

先声明:数据类型是CString,并用vector进行操作,算法为逆波兰

我用MFC做的计算器架构比较简单,界面上除括号外的所有按钮都是直接在编辑框中显示,成为一个算术表达式,“=”作为一个响应函数,建立CMyCalculator的实例,再把算术表达式作为参数调用计算函数,得到结果更新显示在界面上。

void CTestDlg::OnBUTTONEqual() //"="的响应函数

{

CMyCalculator myCalculator(m_sTest);

if(myCalculator.IsExpression()) //检查式子的有效性

{

myCalculator.Change(); //算术表达式->逆波兰表达式

m_sTest.Format("%lf",myCalculator.Calculate()); //计算逆波兰表达式得到结果

UpdateData(false);

}

else

{

MessageBox("Wrong Expresion! please input again!");

}

m_sTest="";

}

封装计算功能的CMyCalculator类架构是

class CMyCalculator

{

public:

CMyCalculator(CString m_stest); //传入算术表达式

~CMyCalculator(); //析构释放vector占用的内存

double Calculate(); //计算后缀表达式(逆波兰表达式)

void Change(); //算数表达式转换为后缀表达式

bool IsExpression(); //判断算术表达式是否正确

bool IsOprand(char ); //判断运算符

bool CheckBracket(); //判断括号的正确性

int GetPriority(CString); //获得运算符的优先级

bool IsPriority(CString,CString); //判断运算符之间的优先级大小

private:

CString m_string; //算术表达式

vector <CString> m_vOperator; //运算符栈,压入运算符(包括括号)

vector <CString> m_vOprand; //操作数栈,压入操作数与运算符(不包括括号)

vector <double> m_vResult; //计算后缀表达式

};

接下来是计算器的核心步骤,逆波兰算法的实现和计算:

算术表达式->逆波兰表达式

//1.将运算符(含括号)压入m_vOperator,把操作数(含运算符)压入m_vOprand;

//2.遇'('直接压入运算符栈,遇')'把中间的运算符弹出到操作数栈;

//3.当前元素<栈顶元素时,栈顶元素的运算符压入运算符栈,当前元素再入操作数栈。

void CMyCalculator::Change()

{

m_vOperator.push_back("#");

for(int i=0;i<m_string.GetLength();i++)

{

//若为运算符或括号

if(IsOprand(m_string.GetAt(i))||m_string.GetAt(i)=='('||m_string.GetAt(i)==')')

{

// 若为 '('

if(m_string.GetAt(i)=='(')

{

m_vOperator.push_back(m_string.GetAt(i));

}

//若为')'

if(m_string.GetAt(i)==')')

{

while(m_vOperator.back()!='(')

{

m_vOprand.push_back(m_vOperator.back());

m_vOperator.pop_back();

}

m_vOperator.pop_back();

}

//若为运算符

if(m_string.GetAt(i)!='('&&m_string.GetAt(i)!=')')

{

if(IsPriority(m_string.GetAt(i),m_vOperator.back())) //若当前元素优先级>栈顶元素则入栈

{

m_vOperator.push_back(m_string.GetAt(i));

}

else //若当前元素优先级<=栈顶元素

{

m_vOprand.push_back(m_vOperator.back()); //栈顶元素压入操作数栈

m_vOperator.pop_back(); //删除栈顶元素

m_vOperator.push_back(m_string.GetAt(i)); //当前元素压入运算符栈

}

}

}

//若为数字

else

{

CString str;

while((m_string.GetAt(i)>=48&&m_string.GetAt(i<=57||m_string.GetAt(i)=='.')

&&i<m_string.GetLength()-1) //当为数字或小数点时累加成一个数字的字符串再压入操作数栈

{

str+=m_string.GetAt(i);

i++;

}

if(i==m_string.GetLength()-1) // 防止访问下表越界,而专为最后一位进行判断

{

str+=m_string.GetAt(i);

i++;

}

m_vOprand.push_back(str);

i--; //for循环中会i++,所以这里得先减1

}

}

//将运算符栈中剩余的运算符压入操作数栈

while(m_vOperator.back()!="#")

{

m_vOprand.push_back(m_vOperator.back());

m_vOperator.pop_back();

}

}

计算逆波兰表达式(后缀表达式)

//将操作数栈中的后缀表达式依次取出,从左至右扫描

//若遇操作数压入m_vResult栈中

//若遇运算符(只含计算的运算符),则把栈顶俩元素弹出进行运算,把结果压入栈中

//最后在栈中保存的就只有一个元素的栈,把m_vResult[0]返回即可

注意:我的计算器是用vector来操作,入栈,出栈只是形象化的比喻,还有vector的内存需要析构,不然会不断耗尽内存的,其次不能简单的析构,需要调用swap来把vector占用的内存给交换出来再析构,vector的clear只是清除数据,不能释放内存。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: