• 结对项目总结


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

      之前急急忙忙地学习了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();
    	}
    }
    

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

  • 相关阅读:
    面向对象一: 数据加载器完成缓存
    软件开发模式总结
    失业求职随便接个单
    恭喜蓝网5巨头输了
    mysql安装及改端口
    解决NAVICAT 无法连接MYSQL8.0.12_可视化工具无法连接 MYSQL 8.0
    c#截取两个指定字符串中间的字符串
    匹配2关键字得结果
    怎么才能更好伪原创
    AntiCrawlerSolution(反爬解决方案)
  • 原文地址:https://www.cnblogs.com/Dmmuistirci/p/5399025.html
Copyright © 2020-2023  润新知