结对项目终于结束。下面是我们小组程序的总结。

  之前急急忙忙地学习了MFC初步用法,并将之付诸实践,其取得的效果还是很令人满意的,出题小程序有模有样。

  我们程序的思路大概是:以十道题为一次小测,判断对错并查看答案。题库一共有五十道题。当然,我们有自动出题的模块,可以自动生成五十道新题。还有判断题目错误(如除零错误,符号错误等),只是我们自动生成的题目不会存在这些问题>_<

这个是我们的界面,初始界面:

点击“自动出题”按钮会自动生成五十道题,并弹出对话框显示成功,像这样:

然后点击“开始测试”,会在左侧生成十道题,像这样:

可以注意到,“开始测试”按钮变成了“再次测试”,并且变灰了,不能按下。只有再答完题确认提交之后才会亮起,并显示下一道题:

答案会依序显示在右侧“答案”框中。若点击再次测试,则界面会回到上一张图那样。

如果完成五十道题的测试,则会显示这个:

当然可以通过重新生成五十道题,继续测试。

若想退出程序,则可以点击“退出测试”,或右上角的“X”。

 

下面是具体的代码:

这个是核心的计算器类,包括计算算式(中缀转后缀,后缀计算等),判断错误等功能:

class Calculator					//核心计算器类 
{
public:
	//辅助计算参数 
	double result;					//计算结果 
	fenshu fresult;					//分数计算结果 
	MyError Error;					//计算过程中是否有错误 
	string str;						//存放中缀表达式 

	Calculator(string s) :fresult(1, 1) {			//计算器初始化 
		u = new unit();
		str = s;
		accuracy = 3;
		maxunit = 80;
		daterange = 100000;
		clear();
	}

	MyError run() {						//计算表达式的值,存入result
		MyError temperror = zzh(str);
		if (temperror != ERROR_NO) {
			Error = temperror;
			result = -11111;
			return Error;
		}
		int i;
		bool b = true;
		for (i = 0; i<str.size(); i++) {	//没有小数点,就计算分数结果 
			if (str[i] == '.') {
				b = false;
				break;
			}

		}

		if (b) {
			temperror = getFResult();
			//MessageBox(printError(temperror).c_str(), "ErrorError", MB_ICONHAND);
			if (temperror != ERROR_NO) {
				fenshu f(-1, -1);
				fresult = f;
				Error = temperror;
				return Error;
			}
			else if (abs(fresult.fz)>daterange || abs(fresult.fm)>daterange) {
				Error = ERROR_RANGE;
				return Error;
			}
		}
		else {
			temperror = getResult();
			if (temperror != ERROR_NO) {
				Error = temperror;
				result = -11111;
				return Error;
			}
			else if (abs(result)>daterange) {
				Error = ERROR_RANGE;
				return Error;
			}
		}
		return ERROR_NO;
	}

	void clear() {					//清空计算器一切辅助计算参数 
		num = 0;
		Error = ERROR_NO;
		result = 0;
		fenshu f(1, 1);
		fresult = f;
		str = "";
		delete u;
		u = new unit[maxunit];
	}

	void recalculator(string s) {	//重启计算器对象 
		clear();
		str = s;
	}

	string getMyResult() {								//获得计算结果,小数结果或者分数结果 
		int i = 0;
		char s[20];
		string ss;
		for (; i<str.size(); i++) {
			if (str[i] == '.') {
				if (accuracy != -1)							//判断精度并输出 
					sprintf(s, "%.*lf", accuracy, result);
				else
					sprintf(s, "%g", result);
				ss = s;
				return ss;
			}
		}
		ss = fresult.getfenshu();
		return ss;
	}

	MyError setDateRange(int type) {				//设置数据范围 
		if (0<type) {
			daterange = type;
			return ERROR_NO;
		}
		else
			Error = ERROR_SET;
		return ERROR_SET;
	}

	MyError setMaxUnit(int num) {				//设置最大识别数量 
		if (0<num&&num <= 80) {
			maxunit = num;
			u = new unit[maxunit]; 			//清空后缀表达式 
			this->num = 0;
			return ERROR_NO;
		}
		else
			Error = ERROR_SET;
		return ERROR_SET;
	}

	MyError setAccuracy(int a) {					//设置精度 
		if (a >= -1 && a <= 6) {
			accuracy = a;
			return ERROR_NO;
		}
		else
			Error = ERROR_SET;
		return ERROR_SET;
	}

private:
	//非辅助计算参数,设置后,除非重复设置,否则不会被clear之类的清除 
	int daterange;					//算式参数中数据的范围 
	int maxunit;					//算式参数最多能识别的字符数量 
	int accuracy;					//小数精确位数,-1为不精确,即去掉所有末尾的0,其他数字即小数点后保留的位数 

									//辅助计算参数 
	unit *u;						//存储后缀表达式 
	int num;						//后缀表达式unit数量 

	MyError zzh(string s) {						//中缀表达式转后缀表达式
		if (s.size()>maxunit) {
			return ERROR_STRING;				//error,传入的算式长度超过设置的最大识别数量
		}
		char c;
		char *temp1 = new char[maxunit];
		double temp;
		string stemp;
		stack<char> st;
		while (!s.empty()) {					//如果字符串不为空则继续循环 
			c = s[0];
			if (isoperator(c)) {				//是操作符 
				s.erase(0, 1);				//从string中删除操作符  
				if (pushintostack(c, &st) == ERROR_OPERATOR)
					return ERROR_OPERATOR;
			}
			else if (isnum(c)) {							//是数字 
				stringstream sst(s);
				sst >> temp;
				sprintf(temp1, "%g", temp);
				stemp = temp1;
				s.erase(0, stemp.size());	//从string中删除数字
				sst.clear();
				u[num++].set(temp);			//存储数字到栈中 
			}
			else {
				return ERROR_STRING;
			}
		}
		if (pushintostack('#', &st) == ERROR_OPERATOR)
			return ERROR_OPERATOR;
		return ERROR_NO;
	}

	bool isoperator(char c) {				//判断是否是操作符 
		if (c == '+')
			return true;
		if (c == '-')
			return true;
		if (c == '*')
			return true;
		if (c == '/')
			return true;
		if (c == '(')
			return true;
		if (c == ')')
			return true;
		return false;
	}

	bool isnum(char c) {
		if (c >= '0'&&c <= '9')
			return true;
		return false;
	}

	int youxian(char c1, char c2) {			//判断两操作符优先级 
		if (c2 == '#')		//结束符 
			return 0;
		if (c2 == '(')
			return 1;
		if (c2 == ')')
			if (c1 == '(')
				return 2;
			else
				return 0;
		if (c1 == '(')
			if (c2 == '+' || c2 == '-' || c2 == '*' || c2 == '/')
				return 1;
		if (c1 == '*' || c1 == '/')
			return 0;
		if (c1 == '+' || c1 == '-')
			if (c2 == '*' || c2 == '/')
				return 1;
			else if (c2 == '+' || c2 == '-')
				return 0;
		return -1;							//非法运算符 
	}

	MyError pushintostack(char c, stack<char> *st) {		//将操作符执行一系列入栈判断操作 
		char a;
		int y = 0;
		while (!st->empty()) {
			a = st->top();
			y = youxian(a, c);
			if (y == 0) {				//后来的操作符优先级小 
				st->pop();
				u[num++].set(a);
			}
			else if (y == 1) {			//后来的操作符优先级大 
				break;
			}
			else if (y == 2) {			//俩操作符是'('和')'
				st->pop();
				return ERROR_NO;
			}
			else
				return ERROR_OPERATOR;
		}
		st->push(c);
		return ERROR_NO;
	}

	void test() {									//输出后缀表达式,测试用(暂留) 
		int i;
		cout << num << endl;
		for (i = 0; i<num; i++) {
			if (u[i].kind == 1)
				cout << u[i].op << " ";
			else if (u[i].kind == 2)
				cout << u[i].num << " ";
		}
	}

	MyError getResult() {						//由run函数调用,获取小数结果,存入result中 
		int i;
		char op;
		double num1, num2;
		stack<double> st;
		for (i = 0; i<num; i++) {					//处理后缀表达式 
			if (u[i].kind == 2) {				//如果是数字则入栈 
				st.push(u[i].num);
			}
			else if (u[i].kind == 1) {			//如果是操作符,则出栈两个数字 
				op = u[i].op;
				if (st.empty())
					return ERROR_STRING;	//算式非法
				num2 = st.top();
				st.pop();
				if (st.empty())
					return ERROR_STRING;	//算式非法
				num1 = st.top();
				st.pop();
				switch (op) {
				case '+':
					st.push(num1 + num2);
					break;
				case '-':
					st.push(num1 - num2);
					break;
				case '*':
					st.push(num1*num2);
					break;
				case '/':
					if (num2 == 0)
						return ERROR_ZERO;	//除0错误
					st.push(num1 / num2);
					break;
				}
			}
			else
				return ERROR_STRING;		//算式非法
		}
		result = st.top();
		return ERROR_NO;
	}

	MyError getFResult() {						//由run函数调用,获取分数结果,存入fresult中 
		int i;
		char op;
		fenshu f1(1, 1), f2(1, 1);
		stack<fenshu> st;
		for (i = 0; i<num; i++) {
			if (u[i].kind == 2) {				//如果是数字则入栈 
				st.push(fenshu(u[i].num, 1));
			}
			else if (u[i].kind == 1) {			//如果是操作符,则出栈两个数字 
				op = u[i].op;
				if (st.empty())
					return ERROR_STRING;	//算式非法
				f2 = st.top();
				st.pop();
				if (st.empty())
					return ERROR_STRING;	//算式非法
				f1 = st.top();
				st.pop();
				switch (op) {
				case '+':
					st.push(f1 + f2);
					break;
				case '-':
					st.push(f1 - f2);
					break;
				case '*':
					st.push(f1*f2);
					break;
				case '/':
					if (f2.fz == 0){
						return ERROR_ZERO;	//除0错误 
					}	
					st.push(f1 / f2);
					break;
				}
			}
			else
				return ERROR_STRING;		//算式非法
		}
		fresult = st.top();
		return ERROR_NO;
	}

  这个是定义的错误以及显示错误的函数:

enum MyError { ERROR_NO = 0, ERROR_SET = 1, ERROR_OPERATOR = 2, ERROR_ZERO = 4, ERROR_STRING = 8, ERROR_RANGE = 16 };

string printError(MyError me) {
	if (me == ERROR_NO) {
		return "没有错误";
	}
	else if (me == ERROR_SET) {
		return "设置参数非法";
	}
	else if (me == ERROR_OPERATOR) {
		return "操作符非法";
	}
	else if (me == ERROR_ZERO) {
		return "除0错误";
	}
	else if (me == ERROR_STRING) {
		return "算式非法";
	}
	else if (me == ERROR_RANGE) {
		return "计算结果超出范围";
	}
	else {
		return "未定义的错误类型";
	}
	//cout<< "	错误代码:" << me << endl << endl;
}

  因为改用了MFC,所以以前的cout都换成了return,返回string直接显示在MessageBox中。

  下面这个是生成算式的按钮,如之前所言,避免了各种错误(如除零错误,算是非法等)的发生:

void CMFCTest4DlgOnBnClickedAuto()
{
	 TODO 在此添加控件通知处理程序代码
	srand((int)time(0));
	string equation;
	char temp[100];
	ofstream outf(equation.txt);
	int i = 50;
	cout  输入生成算式的数量:;
	cin  i;
	while (i--) {
		equation.clear();
		if (i % 2) {											分数运算 
			char lastop = '+';								上一个运算符 
			int num = random(3) + 4;							算式包含的操作数个数-1 
			sprintf(temp, %d, random(20) + 1); 				第一个操作数 
			equation.append(temp);
			while (num--) {
				int b;
				if (lastop == '')							防止连续除法的出现 
					b = random(2);
				else
					b = random(12);
				switch (b) {
				case 0
				case 4
				case 8
					lastop = temp[0] = '+';
					break;
				case 1
				case 5
				case 9
					lastop = temp[0] = '-';
					break;
				case 2
				case 6
				case 10
					lastop = temp[0] = '';
					break;
				case 3
				case 7
				case 11
					lastop = temp[0] = '';
					break;
				}
				temp[1] = 0;
				equation.append(temp);
				sprintf(temp, %d, random(20) + 1);
				equation.append(temp);
			}
			int k, a = 0;
			for (int j = 0; jequation.size(); j++) {					添加括号 
				if ((equation[j] == '+'  equation[j] == '-') && a == 0) {
					a++;
				}
				else if ((equation[j] == '+'  equation[j] == '-') && a == 1) {
					k = j - 1;											添加左括号 
					while (!isoperator(equation[k - 1]) && k != 0) k--;
					if (equation[k - 1] == '') {
						k--;
						while (!isoperator(equation[k - 1]) && k != 0) k--;
					}
					equation.insert(k, ();

					k = j + 2;											添加右括号 
					while (!isoperator(equation[k + 1]) && k != equation.size() - 1) k++;
					equation.insert(k + 1, ));

					break;
				}
			}
			coutequationendl;
		}
		else {														小数运算 
			char lastop = '+';								上一个运算符 
			int num = random(3) + 4;							算式包含的操作数个数-1 
			int temp1 = random(200) + 1;
			sprintf(temp, %g, temp1  10.0); 				第一个操作数 
			equation.append(temp);
			while (num--) {
				int b;
				if (lastop == '')							防止连续除法的出现 
					b = random(2);
				else
					b = random(12);
				switch (b) {
				case 0
				case 4
				case 8
					lastop = temp[0] = '+';
					break;
				case 1
				case 5
				case 9
					lastop = temp[0] = '-';
					break;
				case 2
				case 6
				case 10
					lastop = temp[0] = '';
					break;
				case 3
				case 7
				case 11
					lastop = temp[0] = '';
					break;
				}
				temp[1] = 0;
				equation.append(temp);
				int temp2 = random(200) + 1;
				if (equation[equation.size() - 1] == '' && (temp1%temp2) != 0) {
					temp2 = temp1  5 + 1;
					while (temp1%temp2) {
						temp2++;
					}
				}
				temp1 = temp2;
				sprintf(temp, %g, temp2  10.0);
				equation.append(temp);
			}
			coutequationendl;
		}
		outf  equation  endl;
	}
	//cout  生成算式成功  endl;
	MessageBox(生成50道算式成功, 提示, MB_ICONINFORMATION);
	GetDlgItem(IDOK2)-EnableWindow(true);
	in.close();
	in.open(equation.txt);
	outf.close();
}

  下面这个是点击“确认提交”按钮之后会发生的事情:

void CMFCTest4DlgOnBnClickedButton10()//确认提交按钮
{
	int a = 0;
	//循环存用户答案
	while (a  10)
	{
		cal.recalculator(str_buf[a]);					//重启计算器,并传入算式参数
		temperror = cal.run();
		MessageBox(printError(temperror).c_str(), ERROR, MB_ICONHAND);
		if (cal.Error != ERROR_NO)						//显示错误
		{
			MessageBox(printError(temperror).c_str(), ERROR, MB_ICONHAND);
		}
		switch (a)
		{
		case 0
			GetDlgItemText(IDC_EDIT1, answer[a]);
			SetDlgItemText(IDC_A1, cal.getMyResult().c_str());
			break;
		case 1
			GetDlgItemText(IDC_EDIT2, answer[a]);
			SetDlgItemText(IDC_A2, cal.getMyResult().c_str());
			break;
		case 2
			GetDlgItemText(IDC_EDIT3, answer[a]);
			SetDlgItemText(IDC_A3, cal.getMyResult().c_str());
			break;
		case 3
			GetDlgItemText(IDC_EDIT4, answer[a]);
			SetDlgItemText(IDC_A4, cal.getMyResult().c_str());
			break;
		case 4
			GetDlgItemText(IDC_EDIT5, answer[a]);
			SetDlgItemText(IDC_A5, cal.getMyResult().c_str());
			break;
		case 5
			GetDlgItemText(IDC_EDIT6, answer[a]);
			SetDlgItemText(IDC_A6, cal.getMyResult().c_str());
			break;
		case 6
			GetDlgItemText(IDC_EDIT7, answer[a]);
			SetDlgItemText(IDC_A7, cal.getMyResult().c_str());
			break;
		case 7
			GetDlgItemText(IDC_EDIT8, answer[a]);
			SetDlgItemText(IDC_A8, cal.getMyResult().c_str());
			break;
		case 8
			GetDlgItemText(IDC_EDIT9, answer[a]);
			SetDlgItemText(IDC_A9, cal.getMyResult().c_str());
			break;
		case 9
			GetDlgItemText(IDC_EDIT10, answer[a]);
			SetDlgItemText(IDC_A10,cal.getMyResult().c_str());
			break;
		}
		if (answer[a].GetString() == cal.getMyResult())
		{
			correct++;
		}
		else
		{
			incorrect++;
		}
		a++;
	}
	
	char buf[2][3];
	sprintf(buf[0], %d, correct);
	sprintf(buf[1], %d, incorrect);
	SetDlgItemText(IDC_CORRECT, buf[0]);//显示正确的题数
	SetDlgItemText(IDC_INCORRECT, buf[1]);//显示错误的题数
	GetDlgItem(IDOK2)-EnableWindow(true);//将“再次测试”按钮设为可以点击
	GetDlgItem(IDC_BUTTON10)-EnableWindow(false);//将自己设为不能点击
}

  其中char型二维数组是存放正确和错误题数的,我设置了两个全局变量,correct和incorrect来记录正确错误题数。

  下面是“开始测试”按钮和“再次测试”按钮:

void CMFCTest4DlgOnBnClickedOk2()		//真正的开始按钮
{
	//TODO 在此添加控件通知处理程序代码
	int e = 0;
	last = false;
	correct = 0;
	incorrect = 0;
	SetDlgItemText(IDC_CORRECT, 0);
	SetDlgItemText(IDC_INCORRECT, 0);
	
	while (e  10)
	{
		if (in  str_buf[e])
		{
			switch (e)
			{
			case 0
				SetDlgItemText(IDC_EDIT1, );
				SetDlgItemText(IDC_A1, 第一题答案);
				SetDlgItemText(IDC_EQUALITY1, str_buf[e].c_str());
				break;
			case 1
				SetDlgItemText(IDC_EDIT2, );
				SetDlgItemText(IDC_A2, 第二题答案);
				SetDlgItemText(IDC_EQUALITY2, str_buf[e].c_str());
				break;
			case 2
				SetDlgItemText(IDC_EDIT3, );
				SetDlgItemText(IDC_A3, 第三题答案);
				SetDlgItemText(IDC_EQUALITY3, str_buf[e].c_str());
				break;
			case 3
				SetDlgItemText(IDC_EDIT4, );
				SetDlgItemText(IDC_A4, 第四题答案);
				SetDlgItemText(IDC_EQUALITY4, str_buf[e].c_str());
				break;
			case 4
				SetDlgItemText(IDC_EDIT5, );
				SetDlgItemText(IDC_A5, 第五题答案);
				SetDlgItemText(IDC_EQUALITY5, str_buf[e].c_str());
				break;
			case 5
				SetDlgItemText(IDC_EDIT6, );
				SetDlgItemText(IDC_A6, 第六题答案);
				SetDlgItemText(IDC_EQUALITY6, str_buf[e].c_str());
				break;
			case 6
				SetDlgItemText(IDC_EDIT7, );
				SetDlgItemText(IDC_A7, 第七题答案);
				SetDlgItemText(IDC_EQUALITY7, str_buf[e].c_str());
				break;
			case 7
				SetDlgItemText(IDC_EDIT8, );
				SetDlgItemText(IDC_A8, 第八题答案);
				SetDlgItemText(IDC_EQUALITY8, str_buf[e].c_str());
				break;
			case 8
				SetDlgItemText(IDC_EDIT9, );
				SetDlgItemText(IDC_A9, 第九题答案);
				SetDlgItemText(IDC_EQUALITY9, str_buf[e].c_str());
				break;
			case 9
				SetDlgItemText(IDC_EDIT10, );
				SetDlgItemText(IDC_A10, 第十题答案);
				SetDlgItemText(IDC_EQUALITY10, str_buf[e].c_str());
				break;
			}
		}
		else
		{
			last = true;
		}
		e++;
	}
	//按完开始,变为”再次测试“后就不能再次按下
	SetDlgItemText(IDOK2, 再次测试);
	GetDlgItem(IDOK2)-EnableWindow(false);
	GetDlgItem(IDC_BUTTON10)-EnableWindow(true);
	if (last)
	{
		MessageBox(没有更多的题了!测试结束!, 提示, MB_ICONINFORMATION);
		GetDlgItem(IDC_BUTTON10)-EnableWindow(false);//当没有更多题时,将“确认提交按钮设为enable。
		in.close();
	}
}

  以上就是我们结对项目主要的内容了。这次项目我没有划水,付出了自己的一份努力,完成之后神清气爽。希望小组项目也能同样顺畅。

posted on 2016-04-16 18:19  Dmmuistirci  阅读(309)  评论(0编辑  收藏  举报