题目描述
- 本次作业要求将四则运算的核心部分采取栈的知识进行解决。即表达式生成的合法性检验、表达式结果计算。
- 学习C++界面编程,可以学QT、MFC或者VS,选择其一即可,用博客记录学习到的知识以及心得体会。
作业要求
- 本次作业要求实现核心算法,请将表达式生成的代码及相关的检验、计算表达式结果的代码贴在博客中,并对代码进行必要的解释。
- 发表一篇博客,博客内容为:提供本次作业的github链接,本次程序运行的截图,对界面编程的探索。
- 作业链接
完整代码链接
代码节选
表达式部分
思路
- 这次表达式的存储、检验和输出都是使用栈来完成的。
- 为了后面表达式格式的检验,即确保表达式是如(a+b)/(c-d)的形式,不会出现“)a+b)”或是其他错乱的情况,所有的数字和运算符都以字符的形式存储,这样便于每个部分的检验。
- 参考16进制,把占用两个字符的10处理成只占用一个字符的'A'。这样表达式所有的元素都是以字符的形式存储,而且表达式长度为11个字符。
- 过程:将随机生成的运算符、数字进行检验后(包括除数不为0,不出现小数结果),都push入栈,复制一份进行格式检验,再将表达式pop出来。
if (j == 0)
{
sta.push("=");
sta.push(")");
if (d == 10)sta.push("A");//对10的处理,push数字进栈
else
{
sprintf(temp, "%d", d);
sta.push(temp);
}
sprintf(temp, "%c", sign2);//push运算符进栈
sta.push(temp);
}
stack tempsta;//复制一份表达式
tempsta.initstack();
tempsta = sta;
while (tempsta.isempty())
{
char t,L=0;
if (tempsta.isempty()) { j = true; break; }//检验括号
else
{
t = tempsta.pop();
if (t == '(') L++;
else { j = true; break; }
}
if (tempsta.isempty()) { j = true; break; }//检验数字
else
{
t = tempsta.pop();
if ((t >= '0'&&t <= '9') || t == 'A')L++;
else { j = true; break; }
}
if (tempsta.isempty()) { j = true; break; }//检验运算符
else
{ t = tempsta.pop();
if (t=='+'||t=='-'||t=='*'||t=='/')L++;
else { j = true; break; }
}
if (L != 11 )j = true;//检验表达式长度
//主函数中输出表达式
while (ex.sta.isempty() == false)
{
t = ex.sta.pop();
if (t == 'A')
cout << "10";
else
cout << t;
}
计算部分
- 这次修改,加强了类的封装。在主函数中接受一个表达式对象,输出一个表达式的值,简化了使用。
- 接受栈以后,把栈的运算符和数字传提取出来,计算后return 最后的结果。
int Calculator::total_result(stack & ex)
{
int x1, x2, x3, x4, count_ab = 0, count_cd = 0;//以下是提取表达式的数字和运算符。
char temp,sign1,sign2,sign3;
temp = ex.pop();
temp = ex.pop();
if (temp == 'A')x1 = 10;
else x1 = temp - '0';
sign1 = ex.pop();
temp = ex.pop();
if (temp == 'A')x2 = 10;
else x2 = temp - '0';
temp = ex.pop();
sign2 = ex.pop();
temp = ex.pop();
temp = ex.pop();
if (temp == 'A')x3 = 10;
else x3 = temp - '0';
sign3 = ex.pop();
temp = ex.pop();
if (temp == 'A')x4 = 10;
else x4 = temp - '0';
//以下是答案的计算
this->getnum(x1, x2);
count_ab = this->calculateResult(sign1);
this->getnum(x3, x4);
count_cd = this->calculateResult(sign3);
this->getnum(count_ab, count_cd);
return this->calculateResult(sign2);
}
//计算部分
void Calculator::getnum(int a1, int a2)
{
a = a1;
b = a2;
}
int Calculator::calculateResult(char sign)
{
switch (sign)//注意除数不能为0,整数输出
{
case '+':result = a + b; break;
case '-':result = a - b; break;
case '*':result = a*b; break;
case '/':result = a / b; break;
}
return result;
}
运行示例
对界面编程的探索
-
先对窗口编程进行了初步的学习,得到博主同意后,推荐一篇博客,可以做出一些简单的窗口程序,初步了解c++的窗口编程:http://blog.csdn.net/sxhelijian/article/details/7558896
-
MFC的一些函数的讲解:http://blog.csdn.net/francisapp/article/details/52764117
-
这次先按照第一篇博客的内容做出了一个小程序,详细的过程都在第一篇博客中:
-
然后按照所学,用MFC编出了这个四则运算器:
-
程序代码
void CMFCcountDlg::OnBnClickedButton1()
{
UpdateData();
Expression Ex;
mex = " ";
while (1)
{
Ex.getsign();
Ex.randomNumber();
if (Ex.generateExpression())continue;
for (int i = 0; i < 11; i++)
{
if (Ex.ch[i] == 'A')
{
mex += "10";
}
else
{
mex += Ex.ch[i];
}
}
for (int i = 0; i < 10; i++)
{
EX[i] = Ex.ch[i];
}
break;
}
UpdateData(FALSE);
}
void CMFCcountDlg::OnBnClickedButton2()
{
UpdateData();
Calculator cal;
Counter cou;
int rightanswer;
mrightanswer=rightanswer = cal.total_result(EX);
cou.getanswer(manswer, rightanswer);
mresult = " ";
if (cou.judge())
{
mresult += "You're right!
";
cou.count_right_answer();
mnumber++;
}
else {
mresult += "It's wrong.";
}
UpdateData(FALSE);
}
遇到问题
- 创建mfc应用程序的时候,出现 “无法找到资源编译器dll,请确保路径正确”的提示框。
- 解决办法:在所有文件中搜索出rcdll.dll,然后将这个文件放入c:program.files.(x86)Microsoft.Visual.Studio.10.0vcin中(或者是提示框中的路径)。
- 在.cpp文件中各个头文件的最后添加了#include "stdafx.h"后,会出现已经声明并且定义了类、变量、函数,但是在编译的时候显示无法找到。
- 解决办法:将#include "stdafx.h"作为第一个头文件。
- 上网查了以后才知道:编译器通过一个头文件stdafx.h来使用预编译头文件。stdafx.h这个头文件名是可以在project的编译设置里指定的。编译器认为,所有在指令#include "stdafx.h"前的代码都是预编译的,它跳过#include "stdafx.h"指令,使用projectname.pch编译这条指令之后的所有代码。因此,所有的MFC实现文件第一条语句都是:#include "stdafx.h"。在它前面的所有代码将被忽略,所以其他的头文件应该在这一行后面被包含。否则,你将会得到“No such file or directory”这样让你百思不得其解的错误提示。
体会
- 这次作业的完成是一个不断遇到问题、解决问题的过程,遇到的问题远比上述的多,但是最后做出来这个四则运算器,还是很开心的。
- 将DOS窗口改成MFC,代码的改动不亚于重写一份代码,需要考虑变量新的特性,比如从窗口接受的变量各个函数都可以使用,使用UpdateData()时所有窗口的变量都会更新。我删除了两个大的部分,一个是交互窗口,一个是主函数,而且计数的过程也更加简洁。但是设置了静态变量略微破坏了类的封装性,是一个让我纠结的问题。
- 由于没有系统地学过MFC,所以完成的过程类似于半学习半尝试,一步一步地将这个四则运算器做出来。虽然界面和功能都很简单,但是将原来DOS窗口的代码移植到MFC中并不是一件简单的事。由于没有主函数,每一个button相当于一个自定义函数,点击后就运行这个自定义函数。这样做,面向对象的感觉就更明显,每个button都是独立的,button与button之间需要消息来联系。
- 就比如这个四则运算软件,点击“出题”,就显示出一个算式,点击“运算”就要将这个算式算出来。“出题”和“运算”这两个button就需要联系。因为对MFC中的数据类型不是特别的了解,所以我建立了一个静态变量作为“消息”用于传递生成的表达式。而这次作业要求修改的代码也正好方便了我在MFC中的计算功能,我只需要传递那个表达式,就可以输出一个正确答案。