结对项目终于结束。下面是我们小组程序的总结。
之前急急忙忙地学习了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();
}
}
以上就是我们结对项目主要的内容了。这次项目我没有划水,付出了自己的一份努力,完成之后神清气爽。希望小组项目也能同样顺畅。
浙公网安备 33010602011771号