课程作业(六)


GitHub develop


表达式部分的实现过程

表达式的生成部分主要过程由流程图展示。完整的表达式可以分解成一个个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实现自动切换语言的操作,因此这次对话框的简单实现并没有碰到什么困难。由于之前的代码中完善了各个类的编写,所以在改写到对话框中时也很方便。但是,对话框的局限也相当明显,之前实现的各个功能若是都铺在一个框中就会显得特别臃肿。我想在熟悉对话框编程之后,就开始把原本的程序改成一个单文档程序。不过界面化还是相当复杂的,现在对各种函数和功能还是一知半解。。


参考链接

VC2008以资源形式实现多语言版本(非Unicode)
VS2010/MFC编程入门教程之目录和总结

posted @ 2017-05-31 12:33  Eventide  阅读(361)  评论(0编辑  收藏  举报