一:项目地址
- psp展示
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 预估耗时(分钟) |
Planning | 计划 | 20 | 30 |
·Estimate | · 估计这个任务需要多少时间 | 1000 | 1800 |
Development | 开发 | 600 | 650 |
· Analysis | · 需求分析 (包括学习新技术) | 180 | 240 |
· DesigSpec | · 生成设计文档 | 60 | 60 |
· Design Review | · 设计复审 | 60 | 60 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 60 | 60 |
· Design | · 具体设计 | 120 | 150 |
· Coding | · 具体编码 | 90 | 180 |
· Code Review | · 代码复审 | 60 | 180 |
· Test | · 测试(自我测试,修改代码,提交修改) | 60 | 180 |
Reporting | 报告 | 240 | 360 |
· Test Repor | · 测试报告 | 120 | 120 |
· Size Measurement | · 计算工作量 | 60 | 60 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 30 | 30 |
合计 | 1000 | 1800 |
二:学习日志
2023.10.16日学习日志
学习时间:19:00-22:00
学习内容:1.根据任务书,完成分工。
2.设计相应的代码模块,设计总体框架
3.寻找开源项目,在此基础上完成改善
体会:将任务书中的内容完成细致分类,并进行任务分工,将搜集到的代码进行调试和修改,将最基本的框架先搭建出来。
自我效率评价:在第一次接触软件工程结对项目,对于任务分配不是特别的熟练,但是搜集资料比较顺利,对于前期的准备工作还是十分顺利的。
2023.10.18学习日志
学习时间:09:50--11.55
学习内容:1.编写代码,并完成第一版代码,基本实现四则运算的相关功能。
2.在此基础上,添加新功能
3.实现代码上传到仓库
收获体会:将编写的四则运算进行调试,可以随机进行数值计算,在此过程中了解到了数的随机生成过程,了解到了相关函数使用方法。
自我效率评价:在这段时间进行代码调试很困难,刚开始效率很低,基本都是超出时间完成相应功能。
2023.10.19学习日志
学习时间:09:50-11:50
19:00-22:00
学习内容:1.将代码错误部分进行修改,基本实现任务书上的基本要求。
2.使用vs进行单元测试
3.对程序进行性能分析
收获体会:代码部分功能无法实现,进行相关的代码修改,完成功能要求这个过程很复杂,需要进行弊端的调试修改,当正常功能基本都实现后,进行相关的代码测试,了解了vs的性能测试使用方法。
自我效率评价:基本都超过了预计的时间,遇到的困难很多,各种疑难杂症的处理上花费的时间比较多,所以导致效率不是特别高。
参考资料:Commit message 和 Change log 编写指南 - 阮一峰的网络日志 (ruanyifeng.com)
现代软件工程讲义 2 工程师的能力评估和发展 - SoftwareTeacher - 博客园 (cnblogs.com)
三:解题思路
拿到题目根据需求设计功能。首先需要随机生成数与计算符号,所以需要编写相应的子函数;其次,需要保证运算数与答案在0至100之间,所以需要对随机生成的数进行区间判断;随后,题目数量可以指定,所以需要在主程序中设置循环结构来指定运行次数;接着,需要有答题功能并验证答案是否正确,所以需要设计自动计算函数,让计算机自动计算答案,并与使用者输入答案进行比较,进行判分。最后,对历史成绩进行存储与查询,可以使用结构体存储,记录每次运算的符号、数值与使用者的答案。
四:实现过程
- 代码的实现思路
根据解题思路的描述,我们需要设计五个子函数和一个结构体,结构体中应能包含算式的算术和符号,回答者的答案以及得到的分数。
对于随机生成算术和符号可以使用rand函数。根据srand重新播种,获得不同的值和符号。
计算答案是一个关键函数,设计流程如图所示。
保存练习结果函数即将随机生成的算数与符号送入到函数结构体中进行储存即可。储存可采用二维数组的模式进行。
打印历史结果及调用结构体中已经存储的数即可。
- 本次的任务分工如下:
季行负责:编写readme文件,完成基本程序的搭建,包括四则运算,随机数字生成,生成运算数字和保证计算答案确保在100以内,包含两个运算符号,实现题目的随机生成,存在判错和计分功能,完成基本任务的编写,同时负责主要的代码上传。
项耀东负责:将编写代码的错误进行修改和完善,提高计算性能,同时将运算的优先级过程进行完善,保证四则运算的准确性和精确性,完成四则运算,运算符模块和随机数单元模块的单元测试,并完成整体程序分析和最终的文档编写。
- 分工提交截图
借助使用的文件夹上传,按照代码更新版本上传,上传之后的仓库截图如下
- vs提交仓库记录截图
单元测试部分文章后面会给出相对应的截图展示
- 第一版编写的代码
#include <stdio.h> #include <stdlib.h> #include <time.h> #define MAX_ATTEMPTS 10 #define MAX_SCORE 10 typedef struct { int numCount; int numbers[MAX_ATTEMPTS][3]; char operators[MAX_ATTEMPTS][2]; int userAnswers[MAX_ATTEMPTS]; int scores[MAX_ATTEMPTS]; } ExerciseHistory; // 生成0到100之间的随机整数 int generateRandomNumber() { return rand() % 101; } // 生成随机运算符(+、-、*、/) char generateRandomOperator() { char operators[] = { '+', '-', '*', '/' }; return operators[rand() % 4]; } // 计算题目答案 int calculateAnswer(int* numbers, char* operators, int numCount) { double result = numbers[0]; for (int i = 0; i < numCount - 1; i++) { switch (operators[i]) { case '+': result += numbers[i + 1]; break; case '-': result -= numbers[i + 1]; break; case '*': result *= numbers[i + 1]; break; case '/': result /= numbers[i + 1]; break; default: break; } } return result; } void saveExerciseResult(ExerciseHistory* history, int numCount, int numbers[3], char operators[2], int userAnswer, int score) { if (history->numCount < MAX_ATTEMPTS) { history->numCount++; int index = history->numCount - 1; for (int i = 0; i < 3; i++) { history->numbers[index][i] = numbers[i]; } for (int i = 0; i < 2; i++) { history->operators[index][i] = operators[i]; } history->userAnswers[index] = userAnswer; history->scores[index] = score; } } void printExerciseHistory(ExerciseHistory* history) { printf("历史成绩:\n"); printf("题目 | 答案 | 得分\n"); for (int i = 0; i < history->numCount; i++) { printf("%d %c %d %c %d | %d | %d\n", history->numbers[i][0], history->operators[i][0], history->numbers[i][1], history->operators[i][1], history->numbers[i][2], history->userAnswers[i], history->scores[i]); } printf("=====================================\n"); } int main() { srand(time(NULL)); int numCount = 3; // 控制操作数的个数 int* numbers = new int[numCount]; char* operators = new char[numCount - 1]; int answer, userAnswer, score; ExerciseHistory history; history.numCount = 0; printf("欢迎来到四则运算练习题库!\n"); printf("===========================\n"); while (1) { for (int i = 0; i < numCount; i++) { numbers[i] = generateRandomNumber(); if (i < numCount - 1) { operators[i] = generateRandomOperator(); } } answer = calculateAnswer(numbers, operators, numCount); if (answer < 0 || answer > 100) { continue; // 答案不在0-100之间,重新生成题目 } printf("题目: %d", numbers[0]); for (int i = 0; i < numCount - 1; i++) { printf(" %c %d", operators[i], numbers[i + 1]); } printf(" = ?\n"); printf("你的答案:"); scanf_s("%d", &userAnswer); if (userAnswer == answer) { printf("回答正确!你得了满分(10分)。\n"); score = MAX_SCORE; } else { printf("回答错误,正确答案是 %d。你得了0分。\n", answer); score = 0; } // 保存题目和得分 saveExerciseResult(&history, numCount, numbers, operators, userAnswer, score); printf("=====================================\n"); printf("是否要查看历史成绩? (1=是, 0=否):"); int choice; scanf_s("%d", &choice); if (choice == 1) { printExerciseHistory(&history); } // 清空输入缓冲区 while (getchar() != '\n'); } delete[]numbers; numbers = NULL; delete[]operators; operators = NULL; return 0; }
- 第一次编写的代码存在计算优先级的问题,改进后
#include <stdio.h> #include <stdlib.h> //调用rand函数 #include <time.h> //得到当前时间 #define MAX_ATTEMPTS 10 #define MAX_SCORE 10 typedef struct { int numCount; int numbers[MAX_ATTEMPTS][3]; //10*3 char operators[MAX_ATTEMPTS][2]; //10*2 float userAnswers[MAX_ATTEMPTS]; //10 float scores[MAX_ATTEMPTS]; //10 } ExerciseHistory; float generateRandomNumber() //生成0-100的随机整数 { return rand() % 101; } char generateRandomOperator() //随机生成符号 { char operators[] = { '+', '-', '*', '/' }; return operators[rand() % 4]; } float calculateAnswer(float* numbers, char* operators, int numCount) //计算结果值 { //float result = numbers[0]; float result = 0; //float mid_result = 0; //for (int i = 0; i < numCount - 1; i++) //{ // switch (operators[i]) // { // case '+': // result += numbers[i + 1]; // break; // case '-': // result -= numbers[i + 1]; // break; // case '*': // result *= numbers[i + 1]; // break; // case '/': // result /= numbers[i + 1]; // break; // default: // break; // } //} // if ((operators[0] == '/') || (operators[1] == '*')||(operators[0] == '*') || (operators[1] == '/')) //有乘除符号的话,如果是数量很多可以遍历方法来找 { if (((operators[0] == '/') || (operators[0] == '*')) && ((operators[1] != '/') && (operators[1] != '*')))//一个乘除号且是前面那个 { switch (operators[0]) { //case '+': // result = numbers[1] + numbers[2]; // break; //case '-': // result = numbers[1] - numbers[2]; // break; case '*': result = numbers[0] * numbers[1]; break; case '/': result = numbers[0] / numbers[1]; break; default: break; } switch (operators[1]) { case '+': result = result + numbers[2]; break; case '-': result = result - numbers[2]; break; //case '*': // result = numbers[2] * result; // break; //case '/': // result =result / numbers[2]; // break; default: break; } } if (((operators[1] == '/') || (operators[1] == '*')) && ((operators[0] != '/') && (operators[0] != '*')))//一个乘除号且是后面那个 { switch (operators[1]) { //case '+': // result = numbers[1] + numbers[2]; // break; //case '-': // result = numbers[1] - numbers[2]; // break; case '*': result = numbers[1] * numbers[2]; break; case '/': result = numbers[1] / numbers[2]; break; default: break; } switch (operators[0]) { case '+': result = numbers[0] + result; break; case '-': result = numbers[0] - result; break; //case '*': // result = numbers[2] * result; // break; //case '/': // result = result / numbers[0]; // break; default: break; } } if (((operators[1] == '/') || (operators[1] == '*')) && ((operators[0] == '/') || (operators[0] == '*')))//两个都是乘除号 { switch (operators[0]) { //case '+': // result = numbers[1] + numbers[2]; // break; //case '-': // result = numbers[1] - numbers[2]; // break; case '*': result = numbers[0] * numbers[1]; break; case '/': result = numbers[0] / numbers[1]; break; default: break; } switch (operators[1]) { //case '+': // result = numbers[1] + numbers[2]; // break; //case '-': // result = numbers[1] - numbers[2]; // break; case '*': result = result * numbers[2]; break; case '/': result = result / numbers[2]; break; default: break; } } } if ((operators[0] != '/') && (operators[0] != '*') && (operators[1] != '*') && (operators[1] != '/')) //只存在加减法的话 { switch (operators[0]) { case '+': result = numbers[0] + numbers[1]; break; case '-': result = numbers[0] - numbers[1]; break; default: break; } switch (operators[1]) { case '+': result = result + numbers[2]; break; case '-': result = result - numbers[2]; break; default: break; } } return result; } void saveExerciseResult(ExerciseHistory* history, int numCount, float numbers[3], char operators[2], float userAnswer, float score) { if (history->numCount < MAX_ATTEMPTS) { history->numCount++; int index = history->numCount - 1; for (int i = 0; i < 3; i++) { history->numbers[index][i] = numbers[i]; } for (int i = 0; i < 2; i++) { history->operators[index][i] = operators[i]; } history->userAnswers[index] = userAnswer; history->scores[index] = score; } if (history->numCount >= MAX_ATTEMPTS) { printf("题目数量已达到上限,请重启\n"); exit(0); } } void printExerciseHistory(ExerciseHistory* history) { printf("历史成绩:\n"); printf("题目 | 答案 | 得分\n"); for (int i = 0; i < history->numCount; i++) { printf("%d %c %d %c %d | %f | %f\n", history->numbers[i][0], history->operators[i][0], history->numbers[i][1], history->operators[i][1], history->numbers[i][2], history->userAnswers[i], history->scores[i]); } printf("=====================================\n"); } int main() { //int x, y; srand((unsigned)time(NULL)); //x = rand(); //y = x%100; x%10生成0-9,x%100生成0-99,rand() % 100+1生成1-100; //printf("%d\n",x); //printf("%d\n", y); int numCount = 3;//算数数量 float* numbers = new float[numCount];//c++ char* operators = new char[numCount - 1];//c++ float answer, userAnswer, score; ExerciseHistory history; history.numCount = 0; int exercise_num = 13;//指定题目数量 // //初始化结束 // for (int count = 0; count < exercise_num; count++) { for (int i = 0; i < numCount; i++) { numbers[i] = generateRandomNumber(); if (i < numCount - 1) { operators[i] = generateRandomOperator(); } } //printf("%f\n", numbers[0]); //printf("%f\n", numbers[1]); //printf("%f\n", numbers[2]); //printf("%c\n", operators[0]); //printf("%c\n", operators[1]); answer = calculateAnswer(numbers, operators, numCount);//由于float后保留6位小数 answer = (int)(answer * 100 + 0.5) / 100.0; //printf("%f\n", answer); //printf("输出结果\n"); //printf("%f %c %f %c %f=%f", numbers[0], operators[0], numbers[1], operators[1], numbers[2], answer); if (answer < 0 || answer > 100)//使得答案在0-100之间 { exercise_num++; continue; // 答案不在0-100之间,重新生成题目 } //答题功能 printf("题目: %f", numbers[0]); for (int i = 0; i < numCount - 1; i++) { printf(" %c %f", operators[i], numbers[i + 1]); } printf(" = ?\n"); printf("你的答案:"); scanf_s("%f", &userAnswer); userAnswer = (int)(userAnswer * 100 + 0.5) / 100.0; if (userAnswer == answer) { printf("回答正确!正确答案是%f,你得了满分(10分)。\n",answer); score = MAX_SCORE; } else { printf("回答错误,正确答案是 %f。你得了0分。\n", answer); score = 0; } //printf("你的得分是%f\n", score); saveExerciseResult(&history, numCount, numbers, operators, userAnswer, score); printf("=====================================\n"); printf("是否要查看历史成绩? (1=是, 0=否):"); int choice; scanf_s("%d", &choice); if (choice == 1) { printExerciseHistory(&history); } // 清空输入缓冲区 while (getchar() != '\n'); } delete[]numbers; numbers = NULL; delete[]operators; operators = NULL; return 0; }
改进后将程序运行,完整代码如上图所示,查看运行界面,分别进行了五组测试截图如下
- (拓展功能)添加答题出错可以设置重试次数
- 运行截图
- 当输入答案正确时
- 四则运算的单元测试截图
将四个运算符进行随机组合进行单元测试,会有16种测试结果,单元测试截图如下
随机数和运算符号生成测试截图
测试截图结果如下
- 性能分析截图
通过调用visual studio自带的性能分析器进行截图如上。
五:总体收获
借助本次任务,让我们对软件开发过程有了一定的了解,在这个过程中将前面学习的软件开发功能测试和程序分析有了更深的认识和应用。并借助工具来分析代码的运行效率,双人合作让我明白了团队合作的重要性,组团取暖可以互帮互助的完成各种问题,比自己一个人的效率高很多,同时明白了在一个项目开发的前提下,前提就是明确任务要求和分工,将自己擅长的部分先提出来,取长补短从而快速的完成任务。
六:补充截图
- readme文件截图
- 相关需求里程碑截图
- 所有的警报截图
- 消除所有的警报截图
代码覆盖率截图