课程第六次作业

课程第六次作业

题目要求;

  • 本次作业要求将四则运算的核心部分采取栈的知识进行解决。即表达式生成的合法性检验、表达式结果计算。
  • 学习C++界面编程,可以学QT、MFC或者VS,选择其一即可,用博客记录学习到的知识以及心得体会。

界面的不足之处:

界面没有实现可以下一题的操作,没有锁定算式不能被操作,所以容易被误操作。使输出答案。

学习链接

git链接

界面展示:


代码展示:


void CMFCApplication2Dlg::OnBnClickedButton1()
{
	UpdateData();
	//expression ;窗口添加的变量
	Problem p; 
	tmp = p.generateExpression();
	expression = tmp.c_str();//将string转变为Cstring	
	UpdateData(false);
}


void CMFCApplication2Dlg::OnBnClickedButton2()
{
	UpdateData();
	Problem p;
	rightanswer = p.calculateResult(tmp);//tmp为全局变量实现题目的传入
	if (answer == rightanswer)//answer为用户在界面输入的答案(窗口添加的变量),rightanswer为正确的答案
	{
		correctnum++;//给窗口添加的变量(正确的题目个数)
	}
	else
		wrongnum++;//错误的答案
	UpdateData(false);
}

核心算法流程图展示:

生成算法:

计算算法:

生成算式部分

//生成算式部分----------------------------------------------------------------------
	int Randomvalue::randomNumber()//用于随机生成数字
	{
		return num[rand() % num.size()];
	}
	char Randomvalue::randomOperation()//用于随机生成运算符
	{
		return sign[rand() % sign.size()];
	}

	template<typename T>//模板
	string CreatExpresstion::Tostring(T i)
	{
		stringstream ss;
		string s;
		ss << i;
		ss >> s;
		return s;
	}

	void CreatExpresstion::generateExpression()//用于生成运算式
	{
		expression = Tostring(R.randomNumber()) + Tostring(R.randomOperation()) + Tostring(R.randomNumber());//利用Tostring函数
		int len = rand() % 5 + 2;
		for (int i = 0; i < len; i++)//add '(' and ')'
		{
			int rdnum = rand() % 10 + 1;
			if (rdnum<2)// 加左边 加括号
			{
				expression = Tostring(R.randomNumber()) + Tostring(R.randomOperation()) + "(" + Tostring(expression) + ")";//连接
			}
			else if (rdnum>8)// 加右边 加括号
			{
				expression = "(" + Tostring(expression) + ")" + Tostring(R.randomOperation()) + Tostring(R.randomNumber());
			}
			else if (rdnum<4)// 加左边 不加括号
			{
				expression = Tostring(R.randomNumber()) + Tostring(R.randomOperation()) + Tostring(expression);
			}
			else if (rdnum>6)// 加右边 不加括号
			{
				expression = Tostring(expression) + Tostring(R.randomOperation()) + Tostring(R.randomNumber());
			}
			// 不加
		}
		cout << expression << " = ";//输出算式
		F.writeFile(out_file_name,expression);//写入文件中
		F.writeFile(out_file_name," = ");
		//myfile << expression << " = ";
		answer = calculateResult();
	}

算式计算部分:

unordered_map< char, unordered_map < char, char > > Priorities;

void InitPriorities()//优先级代码
	{
		Priorities['+']['-'] = '>';
		Priorities['+']['+'] = '>';
		Priorities['+']['*'] = '<';
		Priorities['+']['/'] = '<';
		Priorities['+']['('] = '<';
		Priorities['+'][')'] = '>';//右括号优先级最低

		Priorities['-']['-'] = '>';
		Priorities['-']['+'] = '>';
		Priorities['-']['*'] = '<';
		Priorities['-']['/'] = '<';
		Priorities['-']['('] = '<';
		Priorities['-'][')'] = '>';

		Priorities['*']['-'] = '>';
		Priorities['*']['+'] = '>';
		Priorities['*']['*'] = '>';
		Priorities['*']['/'] = '>';
		Priorities['*']['('] = '<';
		Priorities['*'][')'] = '>';

		Priorities['/']['-'] = '>';
		Priorities['/']['+'] = '>';
		Priorities['/']['*'] = '>';
		Priorities['/']['/'] = '>';
		Priorities['/']['('] = '<';
		Priorities['/'][')'] = '>';

		Priorities['(']['+'] = '<';
		Priorities['(']['-'] = '<';
		Priorities['(']['*'] = '<';
		Priorities['(']['/'] = '<';
		Priorities['(']['('] = '<';
		Priorities['('][')'] = '=';
	}

//算式计算部分----------------------------------------------------------------------
	float CreatExpresstion::calculateResult()
	{
		vector<float> Operands;
		vector<char> Operators;
		float OperandTemp = 0;
		char LastOperator = 0;

		for (int i = 0; i < expression.size(); i++)
		{
			char ch = expression[i];//获取分解算式
			if ('0' <= ch && ch <= '9')//如果为运算数
			{
				OperandTemp = OperandTemp * 10 + ch - '0';
			}
			else if (ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '(' || ch == ')')
			{
				if (ch != '(' && LastOperator != ')')
				{
					Operands.push_back(OperandTemp);//压入运算数栈中
					OperandTemp = 0;
				}
				char Opt2 = ch;
				for (; Operators.size() > 0;)//不为空继续计算
				{
					char Opt1 = Operators.back();
					char CompareRet = Priorities[Opt1][Opt2];//利用运算符优先级
					if (CompareRet == '>')//如果优先级为大于
					{
						float Operand2 = Operands.back();//获取运算数栈顶元素
						Operands.pop_back();//冒掉栈顶元素
						float Operand1 = Operands.back();//获取第二个运算数栈顶元素
						Operands.pop_back();//冒掉运算数
						Operators.pop_back();//冒掉运算符
						float Ret = caculate(Operand1, Operand2, Opt1);//调用计算函数
						Operands.push_back(Ret);//将计算结果压入栈中以便继续计算
					}
					else if (CompareRet == '<')//如果优先级为小于则跳过
					{
						break;
					}
					else if (CompareRet == '=')//如果优先级相等
					{
						Operators.pop_back();
						break;
					}
				}
				if (Opt2 != ')')
				{
					Operators.push_back(Opt2);
				}
				LastOperator = Opt2;
			}
		}
		if (LastOperator != ')')
		{
			Operands.push_back(OperandTemp);
		}
		for (; Operators.size() > 0;)//如果运算符栈不为空继续
		{
			float Operand2 = Operands.back();
			Operands.pop_back();
			float Operand1 = Operands.back();
			Operands.pop_back();
			char Opt = Operators.back();
			Operators.pop_back();
			float Ret = caculate(Operand1, Operand2, Opt);
			Operands.push_back(Ret);
		}
		return (float)((int)(Operands.back() * 100)) / 100;//将计算结果返回为保留两位小数
	}

	float CreatExpresstion::caculate(float Operand1, float Operand2, char Operator)
	{
		if (Operator == '+') return Operand1 + Operand2;
		if (Operator == '-') return Operand1 - Operand2;
		if (Operator == '*') return Operand1 * Operand2;
		if (Operator == '/') return Operand1 / Operand2;
	}
	//----------------------------------------------------------------------------------

操作界面展示:

本次作业的心得:

本次作业实现了栈以及生成算式的核心算法,栈是一种重要的思想,曾经以为很难的计算过程可以利用栈来精巧的解决,而这些是想不到的,在查阅计算器计算过程中发现了这一点,在界面学习的过程中第一次觉得更像在写程序的感觉,界面使这一应用更趋向完整性、有使用价值,在今后的学习中会利用课余的时间学习更加高深精致的界面制作。

原文地址:https://www.cnblogs.com/mercuialC/p/6925902.html