现代软件工程HW2:结对编程-生成五则运算式-Core10组 [PB16110698+PB16120162]

作业具体要求点 这里

Core组要求:

1.Calc()

这个Calc 函数接受字符串的输入(字符串里就是算术表达式,例如 “5*3.5”,“7/8 - 3/8 ”,“3 + 90 * 0.3”等等),这个模块的返回值是一个字符串。

 2.Setting()

在生成四则运算题目之前,需要对一系列属性进行设置,例如生成题目的数量,操作数的数量,题目中操作数的数值的范围,运算符的种类,(+-*/,是否支持真分数运算,是否支持小数运算,是否支持乘方运算……

注:

题目中操作数的数值范围对于真分数来说是指其分母的范围,运算结果可以超出指定范围

当题目中包含小数时,答案亦以小数形式给出,结果为无限小数时保留小数点后两位

乘方运算的幂次方为小于5的自然数

 3.Generate()

进行一些设置之后,就可以开始生成所需要的题目了,题目增加以下限制

  • 生成的题目计算过程中不能产生负数

  例如1 - 2 + 3 =,3 + (4 - 5) =,因为计算过程中产生了负数,都是无效的题目

  • 生成的题目中不能包括不必要的括号

  例如 1 + (2 * 3),(1 + 2)+ 3

  其中括号为不必要的括号,不能生产此类题目

  • 程序一次运行生成的题目不能重复

  即任何两道题目不能通过有限次交换+和×左右的算术表达式变换为同一道题目。例如,23 + 45 = 和45 + 23 = 是重复的题目,6 × 8 = 和8 × 6 = 也是重复的题目。3+(2+1)和1+2+3这两个题目是重复的,由于+是左结合的,1+2+3等价于(1+2)+3,也就是3+(1+2),也就是3+(2+1)。但是1+2+3和3+2+1是不重复的两道题,因为1+2+3等价于(1+2)+3,而3+2+1等价于(3+2)+1,它们之间不能通过有限次交换变成同一个题目。

实现以上接口,编写API文档,供UI组使用(亦可自行增加功能)。

 

PSP表格:

 

一、需求分析

  ①考虑本次作业的主要功能是接受setting、生成表达式,具体的Calc运算函数其实和整体关系不大,优先设计Class和setting。经过商讨,我们决定将Class名定为QuestionCore类,其中设置供setting使用的九个变量:

1.生成问题数量(int)questionNum 初值:40

2.最多操作符数(int)maxOpNum 初值:6

3.算式中数字最小值(int)minData 初值:0

4.算式中数字最大值(int)maxData 初值:15

5.操作符字符串,包括“+-*/^”(string)ops 初值:“+-*/^”,支持乱序输入,根据输入的字符串中包含的运算符进行生成算式

6.是否开启分数运算(bool)fracFlag 初值:true

7.小数精度(int)precise 初值:2

8.题目文件路径 (string)QPath 初值:Questions.txt

9.答案文件路径 (string)APath 初值:Answers.txt

这九个变量足以控制实现作业中的各种要求。同时,设置vector<string>容器的变量来存储结果。相应地设置相关函数。

  ②考虑生成运算式的方式,想起学习数据结构时“栈”一章里便是将生成运算式作为例题,故重拾谭浩强,初步预定采用栈来生成运算式。后来在组里讨论交流了一下,发现树结构更适合多运算式的随机生成,于是决定以树结构存储、栈形式导出,实现随机生成运算式的功能。树的结点定义结构体,索性定成一个Class:questionNode,其中包含value值、左孩子lchild、右孩子rchild和判别是数字结点还是运算符结点的bool变量opsFlag。

  ③考虑Calc函数的具体功能,是要对一个输入的式子进行运算,返回string形式的结果。基础运算即不涉及分数的基础四则运算+乘方,通过atoi、to_string等函数不难做到,拆分到CalcNum、CalcStr两个函数中,分别对输入的数字、字符串求解。 追加功能要求支持分数运算,即需要通分、设置带分数等,故加入约分模块,辗转相除法求出最大公约数后进行约分,在Calc中实现。

  ④考虑封装和对接要求,应该着手学习将CPP封装为dll的方法,并且要及时发布、写好API文档,尽早对接、互相完善。由于接口定义模糊不清,可以预计各组的接口都会有所不同,应当早做准备。

  ⑤特殊之处:结对编程。需要收集一些结对编程的技巧和经验,合理分工,共同进步。

 

二、具体设计

  ①设计两个类:questionNode和questionCore,Node是树节点,如上所述;Core是主要变量和函数的类,包含setting所需的控制变量和实现各种功能的函数。

  ②Core类中包含树节点Node* questions和string容器Answer,分别负责生成树、存树、操作树与存储生成式子的答案。生成的式子由getQue函数配合对树的操作得到,保存在string 的容器finalQue中。这样我们就可以通过生成一定数目的式子分别将结果和表达式放入Answer和finalQue,达成功能。树的操作有各种结点遍历、检验、转换成string等。

  ③考虑计算优先级和括号问题。括号要加但不能乱加,我们商量后采用一个比较笨的办法:对每个由其他运算符和数字组建好的式子,先随机生成一定数量的成对括号,找位置往式子里填。填完后,对于无须加括号的,将括号成对删除。此处需要注意的是中途新增需求:乘方运算的右边必须是小于五的自然数,也就是说括号落位还要避免落在乘方后边。

  ④考虑筛选合格算式问题,由算式中各阶段运算非负,设计isPositive函数来判断是否每一阶段都不为负。设计函数判断两颗运算式树是否在本次作业的意义下等同。由于在树生成和转化时逐个判断算式合格过于繁琐,我们采用过量生成、取用合格算式的策略。若合格算式不够多,则再次生成补充。

  ⑤考虑对接问题,设计setting函数参数如上所述,设计getResultFile函数,直接将两个vector中的数据分别导出到客户输入的路径下设定的文件中。

三、具体编码

  整体文件由question.h、question.cpp、main.cpp组成。question.h中声明了类和各类函数;除了构造和析构函数外,其他函数的实现均在question.cpp中进行。main函数主要用于测试功能。代码分别如下。

question.h

头文件已添加预编译头,可编译为dll文件。

  1 #include "Core10.h"
  2 //random a~b
  3 int questionCore::random(int a, int b)
  4 {
  5     return (rand() % (b - a + 1)) + a;
  6 }
  7 
  8 //is operator
  9 bool questionCore::isOp(char ch)
 10 {
 11     auto it = find(ops.begin(), ops.end(), ch);
 12     if (it == ops.end())
 13         return false;
 14     return true;
 15 }
 16 
 17 //is num
 18 bool questionCore::isNum(char ch)
 19 {
 20     if (ch >= '0'&&ch <= '9')
 21         return true;
 22     return false;
 23 }
 24 
 25 //is decimal
 26 bool questionCore::isDeci(string str)
 27 {
 28     for (int i = 0; i < str.size(); i++)
 29     {
 30         if (str[i] == '.')
 31             return true;
 32     }
 33     return false;
 34 }
 35 
 36 //op's priority, higher means prior
 37 int questionCore::priority(char op)
 38 {
 39     switch (op)
 40     {
 41     case '#':return -2;
 42     case '\0':return -1;
 43     case '(':return 0;
 44     case ')':return 0;//avoid ( ) be put out
 45     case '+':
 46     case '-':return 1;
 47     case '*':
 48     case '%':
 49     case '|':
 50     case '/':return 2;
 51     case '^':return 3;
 52     default:return 4;
 53     }
 54 }
 55 
 56 // return (a op b)
 57 double questionCore::CalcNum(double a, double b, char op)
 58 {
 59     switch (op)
 60     {
 61     case '+':return (a + b);
 62     case '-':return (a - b);
 63     case '*':return (a * b);
 64     case '|':return a / b;
 65     case '/':return a / b;
 66     case '%':return (int)a % (int)b;
 67     case '^':return pow(a, b);
 68     default:return a;
 69     }
 70 }
 71 
 72 //return answer of que
 73 double questionCore::CalcStr(string & que)
 74 {//OPTR: ops' stack; OPEN: nums' stack
 75     int posTmp = que.find("*-");
 76     if (posTmp != -1)
 77         return 1;
 78     que.push_back('\0');
 79     stack<char> OPTR; OPTR.push('#');
 80     stack<double> OPEN;
 81     char ch = 'c', chTmp;
 82     double num1, num2;
 83     int pos = 0;
 84 
 85 
 86 
 87     while (ch != '\0' || OPTR.top() != '#')
 88     {
 89         if (ch != '\0') { ch = que[pos]; pos++; }
 90         if (!isOp(ch) && ch != '\0')
 91         {
 92             if (ch == ' ') continue;
 93             else numPush(OPEN, que, pos);
 94             continue;
 95         }
 96         switch (ch)
 97         {
 98         case '(':OPTR.push(ch); break;
 99         case ')':
100             chTmp = OPTR.top();
101             while (chTmp != '(')
102             {
103                 if (OPEN.empty())
104                     throw "wrong form";
105                 num1 = OPEN.top(); OPEN.pop();
106                 if (OPEN.empty())
107                     throw "wrong form";
108                 num2 = OPEN.top(); OPEN.pop();
109                 OPEN.push(CalcNum(num2, num1, chTmp));
110                 OPTR.pop();
111                 chTmp = OPTR.top();
112             }
113             OPTR.pop();
114             break;
115         default:
116             chTmp = OPTR.top();
117             while (priority(chTmp) >= priority(ch))
118             {
119                 OPTR.pop();
120                 if (OPEN.empty())
121                     throw "wrong form";
122                 num1 = OPEN.top(); OPEN.pop();
123                 if (OPEN.empty())
124                     throw "wrong form";
125                 num2 = OPEN.top(); OPEN.pop();
126                 OPEN.push(CalcNum(num2, num1, chTmp));
127                 chTmp = OPTR.top();
128             }
129             if (ch != '\0')OPTR.push(ch);
130             break;
131         }
132     }
133     return OPEN.top();
134 }
135 
136 //push que's nums into stackT
137 void questionCore::numPush(stack<double> & stackT, string que, int & pos)
138 {
139     string strT;
140     char ch;
141     while (!isOp(que[pos - 1]) && que[pos - 1] != '\0')
142     {
143         ch = que[pos - 1];
144         strT.push_back(ch);
145         pos++;
146     }
147     pos--;
148     stackT.push(atof(strT.c_str()));
149 }
150 
151 //find max divisor
152 int questionCore::getDivisor(string str)
153 {
154     string numTmp;
155     int times = 1;
156     int divisorTmp = 1;
157     int strLen = str.size();
158     int backNum = 0;
159     for (int i = 0; i < strLen; i++)
160     {
161         if (str[i] == '|' || str[i] == '/')
162         {
163             if (str[i + 1] == '(')
164             {
165                 i++;
166 
167                 while (i <= strLen)
168                 {
169                     if (str[i] == '(')backNum++;
170                     if (str[i] == ')')backNum--;
171                     if (str[i] == '|' || str[i] == '/')divisorTmp *= getDivisor(str.substr(i));
172                     if (divisorTmp == 0 || divisorTmp>maxRange || divisorTmp<0)
173                         return 1;
174 
175                     numTmp.push_back(str[i]);
176                     if (backNum == 0)break;
177                     i++;
178                 }
179             }
180             else
181             {
182                 while (isNum(str[i + 1]) && i + 1 <= strLen)
183                 {
184                     numTmp.push_back(str[i + 1]);
185                     i++;
186                 }
187 
188             }
189             numTmp.insert(0, 1, '(');
190             times *= int(CalcStr(numTmp.append(")*").append(to_string(int(divisorTmp)))));
191             if (times == 0 || times>maxRange || times<0)
192                 return 1;
193             numTmp.clear();
194         }
195     }
196     if (times == 0 || times>maxRange || times<0)
197         return 1;
198     return times;
199 }
200 
201 //Euclidean algorithm, zhanzhuanxiangchufa
202 int questionCore::gcd(int a, int b)
203 {
204     if (b != 0)
205         return gcd(b, a % b);
206     else
207         return a;
208 }
209 
210 //judge if elems in question are positive 
211 bool questionCore::isPositive(questionNode* root, double &res)
212 {
213     double res1 = 0, res2 = 0;
214     bool flag1, flag2;
215     if (root == NULL) return false;
216     if (root->opsFlag == true)
217     {
218         flag1 = isPositive(root->lchild, res1);
219         flag2 = isPositive(root->rchild, res2);
220         res = CalcNum(res1, res2, char(root->value));
221         if (res < 0 || res>maxRange)return false;
222         else return flag1 && flag2;
223     }
224     else
225     {
226         res = root->value;
227         return true;
228     }
229 }
230 
231 //generate tree from ques, if success return ture
232 bool questionCore::generateTree(vector<string> & ques)
233 {
234     vector<string> res;
235 
236     string que, numStr;
237     stack<char> OPTR;
238     stack<questionNode*> OPEN;
239     questionNode *node1, *node2, *tempNode;
240 
241 
242     for (int i = 0; i < ques.size(); i++)
243     {
244         que = ques[i];
245         que.push_back('\0');
246         OPTR.push('#');
247 
248         char ch = 'c', chTmp;
249         int pos = 0;
250 
251 
252         while (ch != '\0' || OPTR.top() != '#')
253         {
254             if (ch != '\0')ch = que[pos++];
255 
256             if (!isOp(ch) && ch != '\0')
257             {
258                 if (ch == ' ') continue;
259                 else
260                 {
261                     while (!isOp(que[pos - 1]) && que[pos - 1] != '\0')
262                     {
263                         numStr.push_back(que[pos - 1]);
264                         pos++;
265                     }
266                     pos--;
267                     tempNode = new questionNode(atoi(numStr.c_str()), false);
268                     OPEN.push(tempNode);
269                     numStr.clear();
270                 }
271                 continue;
272             }
273             switch (ch)
274             {
275             case '(':OPTR.push(ch); break;
276             case ')':
277                 chTmp = OPTR.top();
278                 while (chTmp != '(')
279                 {
280                     node1 = OPEN.top(); OPEN.pop();
281                     node2 = OPEN.top(); OPEN.pop();
282                     tempNode = new questionNode(int(chTmp), true, node2, node1);
283                     OPEN.push(tempNode);
284                     OPTR.pop();
285                     chTmp = OPTR.top();
286                 }
287                 OPTR.pop();
288                 break;
289             default:
290                 chTmp = OPTR.top();
291                 while (priority(chTmp) >= priority(ch))
292                 {
293                     OPTR.pop();
294                     node1 = OPEN.top(); OPEN.pop();
295                     node2 = OPEN.top(); OPEN.pop();
296                     tempNode = new questionNode(int(chTmp), true, node2, node1);
297                     OPEN.push(tempNode);
298                     chTmp = OPTR.top();
299                 }
300                 if (ch != '\0')OPTR.push(ch);
301                 break;
302             }//switch
303         }//while
304         questions.push_back(OPEN.top());
305         OPEN.pop();
306     }
307     return true;
308 }
309 
310 //judge if 2 trees are the same
311 bool questionCore::isSame(questionNode* root1, questionNode* root2)
312 {
313     if (root1 == NULL && root2 == NULL)
314         return true;
315     else if (root1 == NULL || root2 == NULL)
316         return false;
317 
318     if (root2->value != root1->value)
319         return false;
320     else
321         return isSame(root1->rchild, root2->rchild) && isSame(root1->lchild, root2->lchild);
322 }
323 
324 //judge if 2 trees are equal
325 bool questionCore::isEqual(questionNode* root1, questionNode* root2)
326 {
327     if (root1 == NULL && root2 == NULL)
328         return true;
329     else if (root1 == NULL || root2 == NULL)
330         return false;
331 
332     bool eFlag1, eFlag2;
333     if (isSame(root1, root2))
334         return true;
335     else
336     {
337         eFlag1 = isEqual(root1->lchild, root2->lchild) && isEqual(root1->rchild, root2->rchild);
338         eFlag2 = isEqual(root1->rchild, root2->lchild) && isEqual(root1->lchild, root2->rchild);
339         return (eFlag1 || eFlag2) && (root1->value == root2->value);
340     }
341 }
342 
343 //judge if tree fit conditions
344 bool questionCore::judgeTree()
345 {
346     int rquesNum = questions.size();
347 
348     for (int i = 0; i < rquesNum; i++)
349     {
350         fitFlag[i] = isPositive(questions[i], result[i]);
351         for (int j = 0; j < i; j++)
352         {
353             if (result[i] == result[j])
354             {
355                 if (isEqual(questions[i], questions[j]))
356                 {
357                     fitFlag[i] = false;
358                     isEqual(questions[i], questions[j]);
359                     break;
360                 }
361             }
362         }
363     }
364     return true;
365 }
366 
367 //generate questions
368 vector<string> questionCore::generateQue(int queNum)
369 {
370     vector<string> res;
371     string strTmp;
372     int num, chOp;
373     string numStr;
374     int leftParenPos, rightParenPos;
375     int opNum, parenNum;
376     bool isPow = false;
377     int powFlag = 0;
378 
379     for (int i = 0; i < queNum; i++)
380     {
381         opNum = random(1, maxOpNum);
382 
383         for (int j = 0; j < opNum; j++)
384         {
385             if (isPow == false)
386             {
387                 num = random(minRange, range);
388                 numStr = to_string(num);
389             }
390             else
391             {
392                 num = random(2, 4);
393                 numStr = to_string(num);
394                 isPow = false;
395             }
396             chOp = ops[random(0, ops.size() - 3 - powFlag)];
397             if (char(chOp) == '^')
398             {
399                 isPow = true;
400                 powFlag = 1;
401             }
402 
403             strTmp.append(numStr);
404             strTmp.push_back(chOp);
405         }
406         num = random(minRange, range);
407         numStr = to_string(num);
408         strTmp.append(numStr);
409 
410 
411         parenNum = random(0, opNum);
412 
413         for (; parenNum>0; parenNum--)
414         {
415             leftParenPos = random(0, strTmp.size() - 1);
416             rightParenPos = random(leftParenPos + 1, strTmp.size());
417 
418             while ((leftParenPos != 0 && (!isNum(strTmp[leftParenPos]) || !isOp(strTmp[leftParenPos - 1]))) || (leftParenPos != 0 && strTmp[leftParenPos] == '^'))
419             {
420                 leftParenPos--;
421             }
422             strTmp.insert(leftParenPos, 1, '(');
423 
424             while (rightParenPos != strTmp.size() && (!isNum(strTmp[rightParenPos - 1]) || !isOp(strTmp[rightParenPos])))
425             {
426                 rightParenPos++;
427             }
428             strTmp.insert(rightParenPos, 1, ')');
429         }
430         res.push_back(strTmp);
431         strTmp.clear();
432         powFlag = 0;
433     }
434     return res;
435 }
436 
437 //transform tree to str
438 void questionCore::treeToStr(questionNode* root, string &pre)
439 {
440 
441     if (root == NULL) return;
442     if (root->opsFlag == false)
443     {
444         pre.append(to_string(root->value));
445         return;
446     }
447 
448 
449     if (root->lchild->opsFlag == false)
450         treeToStr(root->lchild, pre);
451     else if ((priority(root->value) > priority(root->lchild->value)) || (root->value == '^'))
452     {
453         pre.push_back('(');
454         treeToStr(root->lchild, pre);
455         pre.push_back(')');
456     }
457     else
458         treeToStr(root->lchild, pre);
459 
460     pre.push_back(char(root->value));
461 
462     if (root->rchild->opsFlag == false)
463         treeToStr(root->rchild, pre);
464     else if ((priority(root->value) >= priority(root->rchild->value)) || (root->value == '^'))
465     {
466         pre.push_back('(');
467         treeToStr(root->rchild, pre);
468         pre.push_back(')');
469     }
470     else
471         treeToStr(root->rchild, pre);
472 
473     return;
474 }
475 
476 //transform original questions to formal questions
477 vector<string> questionCore::quesToStr(vector<questionNode*> Quess)
478 {
479     string strTmp;
480     string strExm;
481     vector<string> finishStr;
482     int count = 0;
483     int rightNum = 0;
484     bool flag = true;
485     int dot = 0;
486     bool flag2 = true;
487 
488     for (int i = 0; i < Quess.size(); i++)
489     {
490         treeToStr(Quess[i], strTmp);
491         for (int j = 0; j<strTmp.size() - 1; j++)
492             if (strTmp[j] == '^')
493                 if ((strTmp[j + 1] > '4') || (strTmp[j + 1] == '('))
494                 {
495                     flag = false; break;
496                 }
497         if (!flag) {
498             strTmp.clear();
499             flag = true;
500             continue;
501         }
502         strExm = Calc(strTmp);
503         for (int j = 0; j < strExm.size(); j++) {
504             if (strExm[j] == '\'') dot = j;
505             if (strExm[j] == '|')
506                 if (j - dot > 3 || strExm.size() - j > 3)
507                     flag2 = false;
508         }
509         if (!flag2) {
510             strTmp.clear();
511             strExm.clear();
512             flag2 = true;
513             dot = 0;
514             continue;
515         }
516 
517         finishStr.push_back(strTmp);
518         strTmp.clear();
519         count++;
520         if (count >= quesNum)
521             break;
522     }
523     return finishStr;
524 }
525 
526 //below:public function
527 
528 //free and reset
529 bool questionCore::Clear() {
530     int Num = questions.size();
531     for (int i = 0; i < Num; i++)
532     {
533         questionNode* temp = questions[i];
534         deleteQues(temp);
535     }
536     questions.clear();
537     Answer.clear();
538     return true;
539 }
540 
541 //get real fraction and return result
542 string questionCore::Calc(string inQues)
543 {
544     int devisor;
545     int intNum;
546     string tpQues;
547     long res;
548 
549     if (fracFlag == true) {
550         devisor = getDivisor(inQues);
551 
552         if (fracFlag&&devisor > 1 && devisor < maxRange && !isDeci(inQues))//get the real fraction
553         {
554             tpQues.append(to_string(devisor));
555             tpQues.append("*(");
556             tpQues.append(inQues).push_back(')');
557             res = int(CalcStr(tpQues));
558             intNum = gcd(res, devisor);
559             tpQues = to_string(int(res / intNum));
560             if (devisor / intNum != 1)
561             {
562                 if (res > devisor)
563                 {
564                     tpQues.clear();
565                     tpQues.append(to_string(int(res / devisor))).append("'").append(to_string(int(res / intNum - int(res / devisor)*(devisor / intNum)))).append("|").append(to_string(devisor / intNum));
566                 }
567                 else
568                     tpQues.append("|").append(to_string(devisor / intNum));
569             }
570             return tpQues;
571         }
572         else
573         {
574             tpQues = to_string(CalcStr(inQues));
575             for (int i = 0; i < 6 - precise; i++)
576                 tpQues.pop_back();
577             if (precise == 0)
578                 tpQues.pop_back();
579             return tpQues;
580         }
581     }
582     //if not fraction
583     else {
584         tpQues = to_string(CalcStr(inQues));
585         for (int i = 0; i < 6 - precise; i++)//set precise
586             tpQues.pop_back();
587         if (precise == 0)
588             tpQues.pop_back();
589         return tpQues;
590     }
591 }
592 
593 //get questions
594 vector<string> questionCore::getQues() {
595 
596     vector<string> originQues;
597     vector<questionNode*> judgedQues;
598     vector<string> finalQues, tempQues;
599 
600     originQues = generateQue(15 * quesNum);
601     //generate 10 times questions and choose 1/10 from them
602     /*for (int i = 0; i < originQues.size()-1; i++) {
603     if (originQues[i].find("^("))
604     for (int j = i; j < originQues.size() - 2; j++)
605     originQues[j] = originQues[j + 1];
606     }*/
607     /*for (vector<string>::iterator it = originQues.begin();it != originQues.end();)
608     {
609     if ((*it).find("^("))
610     //删除指定元素,返回指向删除元素的下一个元素的位置的迭代器
611     it = originQues.erase(it);
612     else
613     //迭代器指向下一个元素位置
614     ++it;
615     }*/
616 
617     generateTree(originQues);
618     judgeTree();//build and judge
619 
620     for (int i = 0; i < questions.size(); i++)
621     {
622         if (fitFlag[i] == true)
623             judgedQues.push_back(questions[i]);
624     }
625 
626 
627     finalQues = quesToStr(judgedQues);
628 
629 
630 
631     for (int i = 0; i < finalQues.size(); i++)
632     {
633         Answer.push_back(Calc(finalQues[i]));
634     }
635 
636     //add ' ' between ops and nums
637     string strTmp;
638     for (int i = 0; i < finalQues.size(); i++)
639     {
640         strTmp = finalQues[i];
641         for (int j = 0; j < strTmp.size(); j++)
642         {
643             if (strTmp[j] == '|'&&fracFlag == true)    continue;
644 
645             else if (isOp(strTmp[j]) && strTmp[j] != '('&&strTmp[j] != ')')
646             {
647                 strTmp.insert(j, 1, ' ');
648                 strTmp.insert(j + 2, 1, ' ');
649                 j += 3;
650             }
651         }//for
652         finalQues[i] = strTmp;
653     }//for
654 
655     return finalQues;
656 }
657 
658 //get answers
659 vector<string> questionCore::getAns() {
660     return Answer;
661 }
662 
663 //setting
664 bool questionCore::setting(int queNum = 40, int maxOpnum = 6, int MinR = 0, int MaxR = 15, string op = "+-*/^", bool fraction = true, int preci = 2, string QPath = "Questions.txt", string APath = "Answers.txt")
665 {
666     quesNum = queNum;
667     maxOpNum = maxOpnum;
668     minRange = MinR;
669     range = MaxR;
670     ops.clear();
671     for (int i = 0; i < op.size(); i++)
672     {
673         ops.push_back(op[i]);
674     }
675     fracFlag = fraction;
676     precise = preci;
677     QuePath = QPath;
678     AnsPath = APath;
679     setOps();
680     return true;
681 }
682 
683 //add ( ) into ops . if fraction, add function
684 void questionCore::setOps() {
685     if (fracFlag)
686         ops.insert(ops.end(), '|');
687     ops.insert(ops.end(), 1, '(');
688     ops.insert(ops.end(), 1, ')');
689 }
690 
691 //getResultFile, output Questions.txt , Answers.txt
692 void questionCore::getResultFile() {
693     vector<string> Ques = getQues();
694     vector<string> Ans = getAns();
695 
696     ofstream foutQues;
697     foutQues.open(QuePath, ios::out);
698     for (int i = 0; i < Ques.size(); i++) {
699         foutQues << Ques[i] << endl;
700     }
701     foutQues.close();
702 
703     ofstream foutAns;
704     foutAns.open(AnsPath, ios::out);
705     for (int i = 0; i < Ans.size(); i++) {
706         foutAns << Ans[i] << endl;
707     }
708     foutAns.close();
709     Clear();
710 }
question.cpp
 1 #include "question.h"
 2 using namespace std;
 3 
 4 int main()
 5 {
 6     questionCore Work(8);
 7 
 8     Work.setting(40, 10, 4, 10, "+-*/^", true, 2);//num of questions, num of ops, min of nums, max of nums, ops(+-*/^), if need fraction, precise
 9                                                    // int                int            int            int          string            bool            int
10     Work.getResultFile();
11 
12     return 0;
13 }
main.cpp

函数前均已写明功能,实现过程并不算复杂,变量名调整得更易读了。

四、测试

  测试环节由我们2人分工,各自出样例导入setting,观察输出函数结果的情况。有一次结果中突然多了一些负数,我debug半天百思不得其解,队友却很快提醒我:我把CalNum函数中变量的先后顺序搞错了,运算数与被运算数换了位置。这让我节省了非常多的时间。经过重重磨难,我们相互督促、相互鼓励,终于将大部分功能基本完成,需要学习dll等待对接。cpp转dll可以说是我遇见过的最坑的博客问题群,各博主们都在教“一加一”,却没有一份教程详细告诉我们成型的cpp和h文件要怎么转换为dll。因此,我们又花费了大量时间摸索dll文件的制作,比其他组慢了一拍。最后,还是组里的一位同学给了我们帮助,让我们得以从繁琐蒙蔽的dll误导中走出来。今后有空,我要写一份真正有用的cpp转dll博客,不让这样的悲剧再发生。

  对接时,我们先找了组里的一队UI,面对面对接,但他的QT似乎有点问题,经过他一段时间的升级编译器后,我们总算有惊无险地对接成功。随后,我们将头文件、dll和lib文件和API文档保存在压缩文件中,发布到群里,目前它正在等待可爱善良帅气大方好心的UI组宠幸。

五、结对

  ①结对编程在这次作业中最直接的好处就是干的活少了,一起讨论、设计完函数后可以共同实现、轮班,相互测试,完成效率比较高。同时,由于一人编码、一人监督,bug率大大降低,有一次我的if函数中犯了个很低级的逻辑错误,立马就被队友机智地识破并指出,省去了巨大的debug时间开销。

  ②从这次结对中,我学到了敢于担当、积极进取的精神,学会了与他人一同合作、共同开发软件,也发现了自己知识层面的许多不足,需要继续努力。

  ③今后工作中若有机会,我会选择结对编程。

                              图为我帮队友同步审核代码

六、总结反思

  本次结对编程中的收获上文中也说得差不多了,补充一点:学到了dll的相关知识且打算抽空写一篇cpp转dll攻略。总的来说还有一些做得不够好的地方,比如接口的设计除了兼容文件输入、字符串输入外,还能更加人性化一点、增加更多功能方便UI组调用等等,但由于时间确实紧张,没能实现多少。

  对于这门课的看法,我认为这门课确实比较锻炼动手能力,但很多时候一些通知和需求下发得比较晚,任务也比较重,对我们的负担还是相当大的。在有个人作业、结对作业的周,我根本没有时间去碰我们的团队作业,还一定程度上影响了我正常的课业学习。但总体而言,我还是比较喜欢这门课的,希望以后的团队作业中,我们组能同心协力、共创佳绩。

 

 

 

 

posted @ 2018-04-15 20:10  CGYR  阅读(389)  评论(1编辑  收藏  举报