课程作业(六)
表达式部分的实现过程
表达式的生成部分主要过程由流程图展示。完整的表达式可以分解成一个个a+b的小表达式,所以通过生成a+b就可以拼接成一个完整的表达式。在生成过程中,通过对分数、括号的存在与否的各种情况的判断,丰富了表达式的类型,增强了最终结果的随机性。并且,只要各个小表达式是合法的,那么最终拼接而成的表达式就会是合法的,所以不需要对表达式的格式进行合法性检验。
生成一个完整的表达式后,再对其进行唯一性和除零的检验,符合要求的话就放进表达式的集合中,否则就重新生成。
刚生成的表达式为中缀表达式,在计算部分,先将其转换为后缀表达式,再进行得数的计算。表达式的转换和后缀表达式的计算都是通过栈实现的。
代码
这里贴了expression类的除头文件外的完整代码,包含了表达式的生成、检验和计算。
/*************************************************************
文件名:Expression.cpp
作者:许郁杨 日期:2017/05/08
描述: 表达式类
主要功能包括:表达式的生成、检验和计算
作者:许郁杨 日期:2017/05/10
更新:补充了注释,对格式排版进行一些调整
*************************************************************/
#include"expression.h"
#include<iostream>
#include<sstream>
#include<vector>
#include<stack>
using namespace std;
Expression::Expression() { }
/*随机生成一个运算符 日期:2017/05/07
更新:注释补充,格式调整 日期:2017/05/10*/
char Expression::RandomOperation(char ifMultiplyDivide)
{
int tmp;
if (ifMultiplyDivide == 'y')//允许乘除
{
tmp = RandomNumber(1, 4);
switch (tmp)
{
case 1:
{
return '+';
break;
}
case 2:
{
return '-';
break;
}
case 3:
{
return '*';
break;
}
case 4:
{
return '/';
break;
}
}
}
else//不允许乘除
{
tmp = RandomNumber(1, 2);
switch (tmp)
{
case 1:
{
return '+';
break;
}
case 2:
{
return '-';
break;
}
}
}
}
/*判断表达式是否唯一,重复为false,唯一为true 日期:2017/05/07
更新:注释补充,格式调整 日期:2017/05/10*/
bool Expression::IsOnly(string expression)
{
int count = 0;
for (unsigned i = 0; i < m_expressionUint.size(); i++)
{
if (expression != m_expressionUint[i])
{
count++;
}
else
{
break;
}
}
if (count == m_expressionUint.size())//如果为唯一
{
return true;
}
else//如果重复
{
return false;
}
}
/*生成一个中缀表达式 日期:2017/05/07
更新:注释补充,格式调整 日期:2017/05/10*/
string Expression::GenerateInfixExpression(int low, int high, int parameterNumber, char ifMultiplyDivide, char ifFraction, char ifBracket)
{
string expression;//表达式
for (; ;)
{
string parameter1, parameter2;//表达式的参数
bool ifFirst = true;//是否已生成第一个小表达式,是为true,否为false
for (int j = 0; j < parameterNumber - 1; j++)
{
int ntmp;
char sign = RandomOperation(ifMultiplyDivide);//运算符
if (ifFraction == 'y')//允许分数
{
ntmp = RandomNumber(1, 3);
switch (ntmp)
{
case 1://整数和整数
{
stringstream sstmp1, sstmp2;
sstmp1 << RandomNumber(low, high);
sstmp1 >> parameter1;
sstmp2 << RandomNumber(low, high);
sstmp2 >> parameter2;
sstmp1.clear();
sstmp2.clear();
break;
}
case 2://整数和真分数
{
stringstream sstmp;
sstmp << RandomNumber(low, high);
sstmp >> parameter1;
sstmp.clear();
Fraction fraction2;
fraction2.GetFraction(low, high);
fraction2.Simplify();
parameter2 = fraction2.TransferIntoStringNoInt();
break;
}
case 3://分数和分数
{
Fraction fraction1, fraction2;
fraction1.GetFraction(low, high);
fraction1.Simplify();
fraction2.GetFraction(low, high);
fraction2.Simplify();
parameter1 = fraction1.TransferIntoStringNoInt();
parameter2 = fraction2.TransferIntoStringNoInt();
break;
}
}
}
else//不允许分数
{
stringstream sstmp1, sstmp2;
sstmp1 << RandomNumber(low, high);
sstmp1 >> parameter1;
sstmp2 << RandomNumber(low, high);
sstmp2 >> parameter2;
sstmp1.clear();
sstmp2.clear();
}
if (ifBracket == 'y')//允许括号
{
ntmp = RandomNumber(1, 4);
switch (ntmp)
{
case 1://无括号
{
if (ifFirst)
{
expression = parameter1 + sign + parameter2;
ifFirst = false;
}
else
{
expression = expression + sign + parameter1;
}
break;
}
case 2://无括号
{
if (ifFirst)
{
expression = parameter2 + sign + parameter1;
ifFirst = false;
}
else
{
expression = parameter1 + sign + expression;
}
break;
}
case 3://有括号
{
if (ifFirst)
{
expression = "[" + parameter1 + sign + parameter2 + "]";
ifFirst = false;
}
else
{
expression = "[" + expression + sign + parameter1 + "]";
}
break;
}
case 4://有括号
{
if (ifFirst)
{
expression = "[" + parameter2 + sign + parameter1 + "]";
ifFirst = false;
}
else
{
expression = "[" + expression + sign + parameter1 + "]";
}
break;
}
}
}
else//不允许括号
{
ntmp = RandomNumber(1, 2);
switch (ntmp)
{
case 1:
{
if (ifFirst)
{
expression = parameter1 + sign + parameter2;
ifFirst = false;
}
else
{
expression = expression + sign + parameter1;
}
break;
}
case 2:
{
if (ifFirst)
{
expression = parameter2 + sign + parameter1;
ifFirst = false;
}
else
{
expression = parameter1 + sign + expression;
}
}
}
}
}
m_infix = expression;
if ((IsOnly(expression)) && (CalculateResult() != "non_comformance"))//判断新生成的表达式是否重复以及是否出现除0的情况
{
m_expressionUint.push_back(expression);
break;
}
}
return expression;
}
/*将中缀表达式转化为后缀表达式 日期:2017/05/07
更新:注释补充,格式调整 日期:2017/05/10*/
void Expression::TransferInfixIntoPostfix()
{
unsigned i = 0;
int j = 0;
stack<char> signStack;//符号栈
while (i < m_infix.size())
{
if ((m_infix[i] >= '0') && (m_infix[i] <= '9'))//判断数字
{
while ((m_infix[i] >= '0') && (m_infix[i] <= '9'))
{
m_postfix[j] = m_infix[i];
i++;
j++;
}
m_postfix[j] = '!';//标识单个整数
j++;
}
if (m_infix[i] == '(')//判断分数
{
while (m_infix[i] != ')')//将分数作为整体
{
m_postfix[j] = m_infix[i];
i++;
j++;
}
m_postfix[j] = m_infix[i];
i++;
j++;
}
if ((m_infix[i] == '+') || (m_infix[i] == '-'))//判断'+'、'-'
{
while ((!signStack.empty()) && (signStack.top() != '['))
{
m_postfix[j] = signStack.top();
j++;
signStack.pop();
}
signStack.push(m_infix[i]);
}
if ((m_infix[i] == '*') || (m_infix[i] == '/'))//判断'*'、'/'
{
while ((!signStack.empty()) && (signStack.top() != '[') && ((signStack.top() == '*') || (signStack.top() == '/')))
{
m_postfix[j] = signStack.top();
j++;
signStack.pop();
}
signStack.push(m_infix[i]);
}
if (m_infix[i] == '[')//判断'['
{
signStack.push(m_infix[i]);
}
if (m_infix[i] == ']')//判断']'
{
while (signStack.top() != '[')
{
m_postfix[j] = signStack.top();
j++;
signStack.pop();
}
signStack.pop();
}
i++;
}
while (!signStack.empty())//当有残余运算符时
{
m_postfix[j] = signStack.top();
j++;
signStack.pop();
}
m_postfix[j] = '\0';//设置终止符
}
/*计算后缀表达式的值 日期:2017/05/07
更新:注释补充,格式调整 日期:2017/05/10*/
string Expression::CalculateResult()
{
int i = 0;
int point = -1;//栈顶指针
bool ifDivideZero = false;//是否除零,是为true,否为false
Fraction numberStack[kMax];//数栈
TransferInfixIntoPostfix();
while ((m_postfix[i] != '\0') && (i<1000))
{
if ((m_postfix[i] >= '0') && (m_postfix[i] <= '9'))//整数入栈
{
double k = 0;//int会计算出错
while ((m_postfix[i] >= '0') && (m_postfix[i] <= '9'))
{
k = 10 * k + m_postfix[i] - '0';
i++;
}
point++;
numberStack[point].TransferIntIntoFraction(k, 1);
}
else
if (m_postfix[i] == '(')//分数入栈
{
double up = 0, down = 0;//int会计算出错
i++;
while (m_postfix[i] != '\\')
{
up = 10 * up + m_postfix[i] - '0';
i++;
}
i++;
while (m_postfix[i] != ')')
{
down = 10 * down + m_postfix[i] - '0';
i++;
}
point++;
numberStack[point].TransferIntIntoFraction(up, down);
}
else//进行计算
{
point--;
switch (m_postfix[i])
{
case '+':
{
numberStack[point] = numberStack[point] + numberStack[point + 1];
break;
}
case '-':
{
numberStack[point] = numberStack[point] - numberStack[point + 1];
break;
}
case '*':
{
numberStack[point] = numberStack[point] * numberStack[point + 1];
break;
}
case '/':
{
if (numberStack[point + 1].isDivisorZero())//如果除数为零
{
ifDivideZero = true;
}
numberStack[point] = numberStack[point] / numberStack[point + 1];
}
}
}
i++;
}
if ((!ifDivideZero) && (numberStack[point].IsInt()))//如果没有除零以及得数为整数
{
return numberStack[point].TransferIntoString();
}
else
{
return "non_comformance";
}
}
运行结果截图
MFC对话框创建
MFC,全称微软基础类库,是一个微软公司提供的类库(class libraries),以C++类的形式封装了Windows API,并且包含一个应用程序框架,以减少应用程序开发人员的工作量。通过它可以非常方便地进行c++的界面化开发。
由于是初学,这里只整理一些基本的对话框程序的操作。
首先是对话框程序的创建:
先新建一个MFC应用程序
然后选择基于对话框,之后就一方通行了,中间的例如最大、最小化之类的选项可以自行选择。
创建完之后,首先要按照自己的需要在对话框中添加各式各样的小控件,选择视图中的工具箱,或者直接点击侧边栏的工具箱,这样更方便些。
然后通过对各个控件属性的Caption进行更改,就能改变它们显示的文字。粗糙排版如下:
MFC对话框编程
写代码前,先要对控件右键添加变量。类别中选择Value,然后选择类型,填写变量名。这样就能够在程序中改变控件的内容。
双击控件后就能对相应控件添加代码了,这里贴上生成算式的按钮的代码。
void CMFCApplication5Dlg::OnBnClickedButton3()
{
// TODO: 在此添加控件通知处理程序代码
UpdateData();
string tmp;
tmp = expression.GenerateInfixExpression(0, 10, 4, 'y', 'y', 'y');
equation = tmp.c_str();
UpdateData(FALSE);
}
注意点和可能碰到的问题
-
UpdataData():在访问变量前调用UpdateData(TRUE),就能够得到用户在控件中输入的内容;在修改变量后调用UpdateData(FALSE),就能使控件显示更新后的内容。
-
#include "stdafx.h":这个头文件中包含了对MFC标准头文件(如Windows.H、Afxwin.H)预先编译的信息,编译器通过stdafx.h来使用预编译头文件。因此,所有的MFC文件的第一条语句都是:#include "stdafx.h",在它前面的所有代码将被忽略。如果不加就会提示编译错误。
-
有同学在创建MFC项目时可能会碰到找不到rcdll.dll文件的问题。百度上的解决方法主要有两种:
-
第一种是在注册表中找到:HKEY_CURRENT_USER/Software/Microsoft/Microsoft SDKs/Windows在windows项中如果存在以下两项就修改为下列示例,如果没有新建如下两项并设置相应值:
- X86系统:"CurrentVersion"="v7.0a" "CurrentInstallFolder"="C://Program Files//Microsoft SDKs//Windows//v7.0A//"
- X64系统:"CurrentVersion"="v7.0a" "CurrentInstallFolder"="C://Program Files (x86)//Microsoft SDKs//Windows//v7.0A//"
-
第二种是直接把C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\bin
目录下的rcdll.dll拷贝到C:\Program Files(x86)\Microsoft Visual Studio 10.0\VC\bin(路径因人而异) -
最简单的方法还是直接找有rcdll.dll文件的同学拷贝一份过去,更简单方便。这里提供我上传的一份的链接:百度云 密码:l2u3
代码
这里贴上两个按钮的代码。
Expression expression;
void CMFCApplication5Dlg::OnBnClickedButton3()
{
// TODO: 在此添加控件通知处理程序代码
UpdateData();
string tmp;
tmp = expression.GenerateInfixExpression(0, 10, 4, 'y', 'y', 'y');
equation = tmp.c_str();
UpdateData(FALSE);
}
void CMFCApplication5Dlg::OnBnClickedButton1()
{
// TODO: 在此添加控件通知处理程序代码
UpdateData();
string result = expression.CalculateResult();
CString tmp1;
tmp1 = result.c_str();
if (answer == tmp1)
{
responce = "正确";
}
else
{
CString tmp2;
tmp2 = "错误,正确答案是";
responce = tmp2 + tmp1;
}
UpdateData(FALSE);
}
运行结果截图
心得体会
之前在编写多语言功能时,因为想用stringtable来实现,所以学过MFC中通过stringtable实现自动切换语言的操作,因此这次对话框的简单实现并没有碰到什么困难。由于之前的代码中完善了各个类的编写,所以在改写到对话框中时也很方便。但是,对话框的局限也相当明显,之前实现的各个功能若是都铺在一个框中就会显得特别臃肿。我想在熟悉对话框编程之后,就开始把原本的程序改成一个单文档程序。不过界面化还是相当复杂的,现在对各种函数和功能还是一知半解。。