现代软件工程HW2:结对编程-生成五则运算式-Core10组 [PB16110698+PB16120162]
作业具体要求点 这里
Core组要求:
1.Calc()
2.Setting()
3.Generate()
一、需求分析
①考虑本次作业的主要功能是接受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函数主要用于测试功能。代码分别如下。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
头文件已添加预编译头,可编译为dll文件。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
函数前均已写明功能,实现过程并不算复杂,变量名调整得更易读了。
四、测试
测试环节由我们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组调用等等,但由于时间确实紧张,没能实现多少。
对于这门课的看法,我认为这门课确实比较锻炼动手能力,但很多时候一些通知和需求下发得比较晚,任务也比较重,对我们的负担还是相当大的。在有个人作业、结对作业的周,我根本没有时间去碰我们的团队作业,还一定程度上影响了我正常的课业学习。但总体而言,我还是比较喜欢这门课的,希望以后的团队作业中,我们组能同心协力、共创佳绩。