个人项目复用代码实现结对编程项目

前言:

  在个人项目中,实现小初高数学出题程序只需要命令行,但在结对编程中需要带图形用户界面,并且用户对象不再是出题老师,而是小初高学生。因为在个人项目中,两人分别使用C++和Python语言来编写的,而C++的图形用户界面感觉晦涩难懂,所有一致决定使用Python来开发结对编程项目,使用的工具为Python自带的Tkinter

结对编程项目要求:

用户:

  小学、初中和高中学生。

功能:

  1、用户注册功能。用户提供手机号码,点击注册将收到一个注册码,用户可使用该注册码完成注册;

  2、用户完成注册后,界面提示设置密码,用户输入两次密码匹配后设置密码成功。密码6-10位,必须含大小写字母和数字。用户在登录状态下可修改密码,输入正确的原密码,再输入两次相同的新密码后修改密码成功;

  3、密码设置成功后,跳转到选择界面,界面显示小学、初中和高中三个选项,用户点击其中之一后,提示用户输入需要生成的题目数量;

  4、用户输入题目数量后,生成一张试卷(同一张卷子不能有相同题目,题目全部为选择题),界面显示第一题的题干和四个选项,用户选择四个选项中的一个后提交,界面显示第二题,...,直至最后一题;

扫描二维码关注公众号,回复: 7362933 查看本文章

  5、最后一题提交后,界面显示分数,分数根据答对的百分比计算;

  6、用户在分数界面可选择退出或继续做题;

  7、小初高数学题目要求见个人项目。

 

项目原型:

  在正式动工前,我们首先在纸上画了软件原型图,如下:

 

   其中用红色线将该项目分为三个部分,两人分工编写

第一部分,登陆:

  由于未能掌握数据库的处理,在保存账号密码的时候,使用Excel表格实现

  在登陆界面只会验证Excel中是否有该手机号,以及手机号对应的密码是否正确,若没有找到手机号,或者找到手机号但密码对不上,则会弹出一个对话框提示手机号或密码输入错误

  若没有注册手机号,可以点击“注册账号”按钮进入注册界面。在注册界面里,当点击“注册”按钮后会验证手机号、密码是否满足格式要求,这里使用正则表达式来实现验证功能,然后会验证两次输入的密码是否一致,以及验证码是否与发送到手机号的验证码一致。点击“获取验证码按钮”会发送验证码到对应手机号上,这里使用了腾讯云的免费试用发送短信功能,免费试用100条短信。注册完成后,注册界面会关闭,在登陆界面输入正确的手机号和密码即可登陆。

第二部分,选择:

  登陆成功后会进入选择界面,在界面右上角可以修改密码,可以在“小学”,“初中”,“高中”三种难度中选择一种,选择后输入生成题目数量,会验证题目数量是否大于0。

第三部分,做题:

  会按照选择的难度和输入题目的数量生成题目,每次生成一道题,生成一道题的代码复用的是C++的,代码如下:

 

  1 //level为0代表难度为小学,1代表初中,2代表高中 
  2 string GetOneQuestion(int level)
  3 {
  4     stringstream ss;
  5     int bracketNum = 0;    //括号的数量,单位为对
  6     int operateNum;        //操作数
  7     bool leftBracket;    //判断该操作数前是否有左括号,若有,则在该操作数后不加右括号,因为要避免出现“(?)”的情况
  8     bool need = false;    //判断是否满足备注要求,初中题目至少一个根号或平方(概率皆为1/2),高中题目至少一个三角函数
  9     if (level == 0)
 10     {
 11         operateNum = 2+rand()%4;    //小学题目2~5个操作数
 12     }
 13     else
 14     {
 15         operateNum = 1+rand()%5;    //初中和高中题目1~5个操作数
 16     }
 17 
 18     if (operateNum == 1)
 19     {
 20         if (level == 1)
 21         {
 22             if (rand()%2 == 0)
 23             {
 24                 ss << "" << 1+rand()%100;
 25             }
 26             else
 27             {
 28                 ss << 1+rand()%100 << "^2";
 29             }
 30         }
 31         else if (level == 2)
 32         {
 33             string fun;
 34             int num;
 35             //tan值要存在
 36             do
 37             {
 38                 fun = g_triFun[rand()%3];
 39                 num = g_triNum[rand()%46];
 40             }
 41             while (fun == "tan(" && (num+90)%180 == 0);
 42             ss << fun << num << "°" << ')';
 43         }
 44     }
 45     else
 46     {
 47         //生成第一个操作数
 48         if (rand()%4 == 0)
 49         {
 50             ss << '(';
 51             bracketNum++;
 52         }
 53 
 54         if (level == 1 && rand()%4 == 0)      //根号
 55         {
 56             need = true;
 57             ss <<"";
 58         }
 59 
 60         if (level == 2 && rand()%4 == 0)      //操作数,或者sin(?)cos(?)tan(?)
 61         {
 62             need = true;
 63             string fun;
 64             int num;
 65             //tan值要存在
 66             do
 67             {
 68                 fun = g_triFun[rand()%3];
 69                 num = g_triNum[rand()%46];
 70             }
 71             while (fun == "tan(" && (num+90)%180 == 0);
 72             ss << fun << num << "°" << ')';
 73         }
 74         else
 75         {
 76             ss << 1+rand()%100;
 77         }
 78 
 79         if (level == 1 && rand()%4 == 0)                  //平方
 80         {
 81             need = true;
 82             ss << "^2";
 83         }
 84 
 85         //生成后续运算符与操作数
 86         for (int i = 0; i < operateNum; i++)
 87         {
 88             char c = g_operators[rand()%4];                                            //运算符
 89             ss << c;
 90 
 91             if (level == 1 && (rand()%4 == 0 || (!need && i == operateNum-1 && rand()%2 == 0)))      //根号
 92             {
 93                 need = true;
 94                 ss <<"";
 95             }
 96 
 97             leftBracket = false;
 98             if (rand()%4 == 0 && i != operateNum-1)          //左括号
 99             {
100                 ss << '(';
101                 leftBracket = true;
102                 bracketNum++;
103             }
104 
105             if (level == 2 && (rand()%4 == 0 || (!need && i == operateNum-1)))      //操作数,或者sin(?)cos(?)tan(?)
106             {
107                 need = true;
108                 string fun;
109                 int num;
110                 //不能发生除0的情况,并且tan值要存在
111                 do
112                 {
113                     fun = g_triFun[rand()%3];
114                     num = g_triNum[rand()%46];
115                 }
116                 while ((c == '/' && ((fun == "sin(" && num%180 == 0) || (fun == "cos(" && (num+90)%180 == 0)))
117                         ||(fun == "tan(" && (num+90)%180 == 0));
118                 ss << fun << num << "°" << ')';
119             }
120             else
121             {
122                 ss << 1+rand()%100;
123             }
124 
125             if (level == 1 && rand()%8 == 0)                  //平方在操作数右边
126             {
127                 need = true;
128                 ss << "^2";
129             }
130             if (rand()%4 == 0 && bracketNum > 0 && !leftBracket)      //右括号
131             {
132                 ss << ')';
133                 bracketNum--;
134             }
135             if (level == 1 && (rand()%8 == 0 || (!need && i == operateNum-1)))                  //平方有可能在括号右边
136             {
137                 need = true;
138                 ss << "^2";
139             }
140         }
141         while (bracketNum--)
142         {
143             ss << ')';
144         }
145     }
146 
147     ss << '=';
148     if (g_questions.find(ss.str()) != g_questions.end())
149     {
150         return GetOneQuestion(level);
151     }
152     else
153     {
154         g_questions.insert(ss.str());
155         return ss.str();
156     }
157 }

 

  转换为Python代码时会有些不同,因为在个人项目中,只需要考虑出题,而在结对编程中还需要考虑出正确答案与干扰选项。生成题目的流程还是一样的,然后会用到Python的内置函数eval(),函数会把字符串当作正确的表达式来运行,可以借此来获得随机生成的表达式的值,当然前提是在调用eval()函数前要把表达式的根号换成sqrt函数,把圆周率π变成p,其中p=pi,把平方符号换成**2,这样才符合Python的正确表达式形式。通过修改该表达式中某一运算符,分别改成加减乘除,可以得到四个表达式,也就得到四个值,返回四个表达式与四个对应的值。这里没有决定哪个是正确选项,返回之后,程序再生成随机数来决定哪一个表达式是正确答案。

  具体Python代码如下:

  1 operators = ['+', '-', '*', '/']
  2 triFun = ['sin(', 'cos(', 'tan(']
  3 triNum = ['0', 'π/6', 'π/4', 'π/3', 'π/2']
  4 
  5 def createOneQuestion(level):
  6     question = ''
  7     questions = []
  8     answers = []
  9     p = pi
 10     bracketNum = 0
 11     need = False
 12     if level==0:
 13         operateNum = randint(2,5)
 14     else:
 15         operateNum = randint(1,5)
 16     
 17     if operateNum==1:
 18         if level==1:
 19             s = set()
 20             while (len(s)<4):
 21                 if randint(0,7)==0:
 22                     s.add('√('+str(randint(1,100))+'')
 23                 elif randint(0,6)==0:
 24                     s.add('√('+str(randint(1,100))+'²)')
 25                 elif randint(0,1)>0:
 26                     s.add('√('+str(randint(1,100))+')')
 27                 else:
 28                     s.add(str(randint(1,100))+'²')
 29             questions = list(s)
 30             for i in range(4):
 31                 answers.append(eval(questions[i].replace('','sqrt').replace('²','**2')))
 32         else:
 33             s = set()
 34             while (len(s)<4):
 35                 fun = triFun[randint(0,2)]
 36                 num = triNum[randint(0,4)]
 37                 while (fun=='tan(' and num=='π/2'):
 38                     fun = triFun[randint(0,2)]
 39                     num = triNum[randint(0,4)]
 40                 s.add(fun+num+')')
 41             questions = list(s)
 42             for i in range(4):
 43                 answers.append(eval(questions[i].replace('π','p')))
 44                 
 45         return questions, answers
 46     else:
 47         #第一个操作数
 48         if randint(0,3)==0:
 49             question += '('
 50             bracketNum += 1
 51         if level==1 and randint(0,3)==0:
 52             need = True
 53             question += '√('
 54             bracketNum += 1
 55         
 56         if level==2 and randint(0,3)==0:
 57             need = True
 58             fun = triFun[randint(0,2)]
 59             num = triNum[randint(0,4)]
 60             while (fun=='tan(' and num=='π/2'):
 61                 fun = triFun[randint(0,2)]
 62                 num = triNum[randint(0,4)]
 63             question += fun+num+')'
 64         else:
 65             question += str(randint(1,100))
 66             
 67         if level==1 and randint(0,3)==0:
 68             question += '²'
 69         
 70         #生成后续运算符与操作数
 71         indexs = []
 72         for i in range(0,operateNum):
 73             c = operators[randint(0,3)]
 74             question += c
 75             indexs.append(len(question)-1)
 76             
 77             if level==1 and (randint(0,3)==0 or (not need and i==operateNum-1 and randint(0,1)==0)):
 78                 need = True
 79                 question += '√('
 80                 bracketNum += 1
 81                 
 82             if randint(0,3)==0 and i!=operateNum-1:
 83                 question += '('
 84                 bracketNum += 1
 85             
 86             if level==2 and (randint(0,3)==0 or (not need and i==operateNum-1)):
 87                 need = True
 88                 fun = triFun[randint(0,2)]
 89                 num = triNum[randint(0,4)]
 90                 while ((fun=='tan(' and num=='π/2') or (c=='/' and (((fun=='sin(' or fun=='tan(') and num=='0') or (fun=='cos(' and num=='π/2')))):
 91                     fun = triFun[randint(0,2)]
 92                     num = triNum[randint(0,4)]
 93                 question += fun+num+')'
 94             else:
 95                 question += str(randint(1,100))
 96 
 97             if level==1 and randint(0,7)==0:
 98                 need = True
 99                 question += '²'
100             if randint(0,3)==0 and bracketNum>0:
101                 question += ')'
102                 bracketNum -= 1
103             if level==1 and (randint(0,7)==0 or (not need and i==operateNum-1)):
104                 need = True
105                 question += '²'
106 
107         question += ')'*bracketNum
108 #        print(indexs)#输出题目的运算符的位置
109         index = indexs[randint(0,operateNum-1)]
110         temp = operators.copy()
111         shuffle(temp)
112         for i in range(4):
113             question = question[:index]+temp[i]+question[index+1:]
114             try:
115                 if level==0:
116                     answers.append(eval(question))
117                 elif level==1:
118                     answers.append(eval(question.replace('','sqrt').replace('²','**2')))
119                 elif level==2:
120                     answers.append(eval(question.replace('π','p')))
121             except (ZeroDivisionError, ValueError):
122                 answers.append('不存在')
123             questions.append(question)
124         if len(set(answers))<4:
125 #            print('有相同答案')
126 #            for i in range(4):
127 #                print(questions[i], answers[i])
128             return createOneQuestion(level)
129         return questions, answers

  做题时会记录正确与否,做完题后会按照百分制输出分数。然后可以退出程序或者继续做题。

经验教训:

  在开发图形用户界面的时候,先画出一个软件原型图会使得工作事半功倍,编程界面的时候一下子就完成了。但画完原型图还有一个工作要做,因为是分工合作,需要给出互相调用的接口,命名要一致。比如我做了登陆界面,登陆成功要跳转到选择界面,选择界面是队友在搞,这时候需要调用他写的类,类名要一致,这样当我们编好程后直接导入模块就可以进行测试了。

  编写用户界面的时候,最好使用类来编写,这样类成员变量就相当于全局变量,类成员函数之间就可以直接使用类成员变量,而不需要global全局变量,也不需要考虑不同函数摆放的位置。一开始我们直接用函数来生成图形用户界面,但遇到了很多bug,换成类来编写逻辑就十分清晰明了。

猜你喜欢

转载自www.cnblogs.com/wwht233/p/11587547.html