本文主要是介绍基于四则运算项目的结对编程项目作业记录,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
本次作业由郑会吉(D23181103)与刘奇(D22180806)共同完成.
仓库URL:
luiqi314/new四则运算1018
20231025补充根据题目难度打分功能,补充至4.5节。
一、基础工作
1.1 建立代码仓库,new四则运算1018并通过readme对预期的需求进行分析和规划。
图1 建立仓库_new四则运算1018
1.2 按照里程碑方式进行计划管理,初步关联5条需求,并进行了任务分配
图2 在里程碑中初步关联5条需求
1.3实际工作组成
工作内容 | 郑会吉 | 刘奇 |
制定计划 | 根据任务要求制定计划 | 提供设计思路 |
需求分析 | 补充实用易实现的功能:限制做题时间、根据不同难度限制输出结果范围 | 提供基础框架思路 |
实际开发 | 具体的编写程序,分别实现部分模块的功能 | 具体的编写程序,分别实现部分模块的功能 |
软件测试 | 代码审查 | 单元测试 |
文档撰写 | 工作总结 | 博客撰写 |
二、需求分析
第一个版本的程序需要解决最为基础的问题,根据项目需求,从用户使用的角度确定的5个需求如下:
- 运算数为100 以内的数字。
- 需要有答题功能并验证答案是否正确。
- 计算正确率
- 避免题目重复
- 计算答题时间
上述是需求的自然语言描述,可能存在歧义或描述不够详细的地方,为此按GJB-438B对需求规格进行进一步分析。
功能 | SZ-QU用户运行程序后,程序依次生成题目; SZ-CORE用户通过控制台输入自己的计算结果;,控制台输出正确结果以及判断回答是否正确; SZ-TIME完成所有题目后统计所用总时间。 |
性能 | SZ-QU 尽量避免题目重复,回答上一题并有结果后才会生成下一题 SZ-CORE 响应时间小于10ms SZ-TIME 时间精确到ms |
可靠性 | 暂无 |
可维护性 | 为提高可测试性,开发过程中可以将功能分成不同函数单独编程,方便后续单元测试 |
三、初代版本程序(责任人:刘奇)
3.1 解体思路:
首先根据需求要实现可以通过随机数产生不重复的数字组合,然后在数字之间加入随机的操作符,通过比较输入的结果来打分,通过自带的时间函数统计所花时间。
在开源程序的基础上进行功能拓展及完善,开源程序URL:
// 四则运算.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//解决思路:switch进行变量控制,出版本只有加减乘除
//解决思路,0-100以内运算
//程序支持判断对错及累计得分与时间
//解决思路:对错用count计数,最终算出正确率。时间用clock函数计算
//第一版本固定出3道题目,而且不能重复(比如2 + 3 与 3 + 2 算重复的)
//采用不重复随机算法,尽量降低重复。
//想法:支持循环测验。#include <iostream>
#include<cstdlib>
#include<time.h>
#define CLOCKS_PER_SEC ((clock_t)1000)
using namespace std;
int Difficulty(int difficulty)
{int N=0;switch (difficulty){case 1:N = 100;break;case 2:N = 1000;break;case 3:N = 10000;break;default:cout << "请输入正确的难度。" << endl;break;}return N;
}
void Grade(int grade,int numberExercise,int N) {double a1, a2, a3,c,k, correctrate;double duration;//做题花费的时间clock_t start, finish;
int mode, count = 0;switch (grade){case 1://*,/,1start = clock();srand((unsigned)time(NULL));for (int i = 0; i < numberExercise; i++) {a1 = rand() % N;a2 = rand() % N;mode = rand() % 4;switch (mode) //确定运算符{case 0:cout << a1 << "+" << a2 << "=" << endl;c = a1 + a2;break;case 1:c = a1 - a2;cout << a1 << "-" << a2 << "=" << endl;break;case 2:c = a1 * a2;cout << a1 << "*" << a2 << "=" << endl;break;case 3:c = a1 / a2;cout << a1 << "/" << a2 << "=" << endl;break;}cout << "请输入答案:";cin >> k;if (k == c) {cout << "恭喜你答对了,请再接再厉" << endl;count = count + 1;}else if (k != c) {cout << "虽然答错了,但不要灰心哦,正确答案是" << c << endl;}}correctrate = (count * 100) / numberExercise;finish = clock();duration = (double)(finish - start) / CLOCKS_PER_SEC;cout << "恭喜你做完了" << numberExercise << "道题目,做对了" << count << "道题目,正确率为 " << correctrate << "%" << " 一共耗时(s):" << duration << endl;break;}}
int main()
{int T=0;do {int grade, numberExercise, difficulty, N = 0;grade = 1;difficulty = 1;numberExercise = 3;N = Difficulty(difficulty);Grade(grade, numberExercise, N);T++;} while (T==0);system("pause");return 0;
}
在VS2022中进行程序编写及测试:
3.2 代码审查:
第一版程序IDE代码审查结果:
运行code_analyse对代码质量进行分析
warning C4101: “a3”: 未引用的局部变量
warning C26451: 算术溢出: 使用 4 字节值上的运算符 - ,然后将结果转换到 8 字节值。在调用运算符 - 之前将值强制转换为宽类型可避免溢出(io.2)。
运算符 - 之前将值强制转换为宽类型可避免溢出(io.2)。
图3 解决警告
两个waring均解决。
采纳vs推荐的修改方案。
3.3 性能分析
在VS2022的性能探查器得到的结果如下图:
图4 性能分析结果
从DPU的使用量看,函数中主要是外部调用的
图5 语句分析
在控制台输出cout以及比较最终结果是否正确花费了不少时间。为此将system(pause)注释掉,不需要系统的等待时间。
图6 性能变化
初始程序的性能探查,修改后的性能分析如下图:
图7 性能变化
四、版本迭代
4.1增加需求:实现-增加题目难度、运算数个数的选择功能(责任人:郑会吉)
代码如下:
// 四则运算.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//分别能够实现小学一、二、三、四、五年级的四则运算要求, 逐步实现各个年级的难度
//解决思路:switch进行变量控制,年纪用+,-,*,/,运算个数来分类。一年级只有+,-,二年级三个数进行+,-,三年级*,/,四年级三个数运算,五年级小数运算。()加入其中。
//要求能够通过输入来选择不同年级,每个年级还得区分难,中,易三个等级
//解决思路,易0-100以内运算,中,100-1000以内运算,难,1000-10000以上运算
//程序支持判断对错及累计得分与时间
//解决思路:对错用count计数,最终算出正确率。时间用clock函数计算
//一次可以出100道题目,而且不能重复(比如2 + 3 与 3 + 2 算重复的)
//采用不重复随机算法,尽量降低重复。
//充分发挥想象增加满足小学生数学检测需要的功能
//想法:支持循环测验。#include <iostream>
#include<cstdlib>
#include<time.h>
#define CLOCKS_PER_SEC ((clock_t)1000)
using namespace std;
int Difficulty(int difficulty)
{int N=0;switch (difficulty){case 1:N = 100;break;case 2:N = 1000;break;case 3:N = 10000;break;default:cout << "请输入正确的难度。" << endl;break;}return N;
}
void Grade(int grade,int numberExercise,int N) {double a1, a2, a3,c,k, correctrate;double duration;//做题花费的时间clock_t start, finish;
int mode, count = 0;switch (grade){case 1: //+,-,2start = clock();srand((unsigned)time(NULL));for (int i = 0; i < numberExercise; i++) {a1 = rand() % N;a2 = rand() % N;mode = rand() % 2;switch (mode) //确定运算符{case 0:cout << a1<< "+" << a2 << "=" << endl;c = a1+a2;break;case 1:c = a1-a2;cout << a1 << "-" << a2<< "=" << endl;break;}cout << "请输入答案:";cin >> k;if (k == c) {cout << "恭喜你答对了,请再接再厉" << endl;count = count + 1;}else if (k != c) {cout << "虽然答错了,但不要灰心哦,正确答案是" << c << endl;}}correctrate = (count*100) / numberExercise;finish = clock();duration = (double)(finish - start) / CLOCKS_PER_SEC;cout << "恭喜你做完了" << numberExercise << "道题目,做对了" << count << "道题目,正确率为 " << correctrate << "%" <<" 一共耗时(s):"<<duration<<endl;break;case 2://+-,3start = clock();srand((unsigned)time(NULL));for (int i = 0; i < numberExercise; i++) {a1 = rand() % N;a2 = rand() % N;a3 = rand() % (2*N)-N;mode = rand() % 2;switch (mode) //确定运算符{case 0:cout << a1 << "+" << a2 <<"+("<<a3<< ")=" << endl;c = a1+a2+a3;break;case 1:c = a1 - a2+a3;cout << a1 << "-" << a2 <<"+("<<a3<< ")=" << endl;break;}cout << "请输入答案:";cin >> k;if (k == c) {cout << "恭喜你答对了,请再接再厉" << endl;count = count + 1;}else if (k != c) {cout << "虽然答错了,但不要灰心哦,正确答案是" << c << endl;}}correctrate = (count * 100) / numberExercise;finish = clock();duration = (double)(finish - start) / CLOCKS_PER_SEC;cout << "恭喜你做完了" << numberExercise << "道题目,做对了" << count << "道题目,正确率为 " << correctrate << "%" << " 一共耗时(s):" << duration << endl;break;case 3://*,/,2start = clock();srand((unsigned)time(NULL));for (int i = 0; i < numberExercise; i++) {a1 = rand() % N;a2 = rand() % N;mode = rand() % 4;switch (mode) //确定运算符{case 0:cout << a1 << "+" << a2 << "=" << endl;c = a1 + a2;break;case 1:c = a1 - a2;cout << a1 << "-" << a2 << "=" << endl;break;case 2:c = a1 * a2;cout << a1 << "*" << a2 << "=" << endl;break;case 3:c = a1 / a2;cout << a1 << "/" << a2 << "=" << endl;break;}cout << "请输入答案:";cin >> k;if (k == c) {cout << "恭喜你答对了,请再接再厉" << endl;count = count + 1;}else if (k != c) {cout << "虽然答错了,但不要灰心哦,正确答案是" << c << endl;}}correctrate = (count * 100) / numberExercise;finish = clock();duration = (double)(finish - start) / CLOCKS_PER_SEC;cout << "恭喜你做完了" << numberExercise << "道题目,做对了" << count << "道题目,正确率为 " << correctrate << "%" << " 一共耗时(s):" << duration << endl;break;case 4://+,-,*,/,3start = clock();srand((unsigned)time(NULL));for (int i = 0; i < numberExercise; i++) {a1 = rand() % N;a2 = rand() % N;a3 = rand() % (2 * N) - N;mode = rand() % 4;switch (mode) //确定运算符{case 0:cout << a1 << "+(" << a2 <<"*("<<a3<<"))=" << endl;c = a1 + a2*a3;break;case 1:c = a1 - a2/a3;cout << a1 << "-(" << a2 << "/(" << a3 << "))=" << endl;break;case 2:c = a1 * (a2-a3);cout << a1 << "*(" << a2 << "-(" << a3 << "))=" << endl;break;case 3:c = a1 / (a2+a3);cout << a1 << "/(" << a2 << "+(" << a3 << "))=" << endl;break;}cout << "请输入答案:";cin >> k;if (k == c) {cout << "恭喜你答对了,请再接再厉" << endl;count = count + 1;}else if (k != c) {cout << "虽然答错了,但不要灰心哦,正确答案是" << c << endl;}}correctrate = (count * 100) / numberExercise;finish = clock();duration = (double)(finish - start) / CLOCKS_PER_SEC;cout << "恭喜你做完了" << numberExercise << "道题目,做对了" << count << "道题目,正确率为 " << correctrate << "%" << " 一共耗时(s):" << duration << endl;break;case 5://小数运算start = clock();srand((unsigned)time(NULL));for (int i = 0; i < numberExercise; i++) {a1 = rand() % N+ rand() / double(RAND_MAX);//加上0-1的小数,即可。a2 = rand() % N+ rand() / double(RAND_MAX);a3 = rand() % (2 * N) - N+ rand() / double(RAND_MAX);mode = rand() % 4;switch (mode) //确定运算符{case 0:cout << a1 << "+(" << a2 << "*(" << a3 << "))=" << endl;c = a1 + a2 * a3;break;case 1:c = a1 - a2 / a3;cout << a1 << "-(" << a2 << "/(" << a3 << "))=" << endl;break;case 2:c = a1 * (a2 - a3);cout << a1 << "*(" << a2 << "-(" << a3 << "))=" << endl;break;case 3:c = a1 / (a2 + a3);cout << a1 << "/(" << a2 << "+(" << a3 << "))=" << endl;break;}cout << "请输入答案:";cin >> k;if (k == c) {cout << "恭喜你答对了,请再接再厉" << endl;count = count + 1;}else if (k != c) {cout << "虽然答错了,但不要灰心哦,正确答案是" << c << endl;}}correctrate = (count * 100) / numberExercise;finish = clock();duration = (double)(finish - start) / CLOCKS_PER_SEC;cout << "恭喜你做完了" << numberExercise << "道题目,做对了" << count << "道题目,正确率为 " << correctrate << "%" << " 一共耗时(s):" << duration << endl;break;default:cout << "请输入正确的年级。" << endl;break;}
}
int main()
{int T;do {int grade, numberExercise, difficulty, N = 0;cout << "请选择您的当前年级:1:一年级,2:二年级,3:三年级,4:四年级,5:五年级。" << endl;cin >> grade;cout << "请选择难度:1:简单,2:普通,3:困难。" << endl;cin >> difficulty;cout << "请输入你要做的题数:" << endl;cin >> numberExercise;N = Difficulty(difficulty);Grade(grade, numberExercise, N);cout << "如果您想继续做题,请输入1,退出系统请输入其他任意数" << endl;cin >> T;} while (T==1);system("pause");return 0;
}
修改后的代码能够根据输入的年级数确定计算的难度,并根据输入的数确定题目的数量,并限制计算数的大小。
通过fork的分支修改后进行pull request后,由主分支的责任人在Giteediamante审查通过后合并进主分支:
Gitee代码审查:
4.2增加需求:增加防沉迷功能(责任人:刘奇)
考虑到小学123年级比较低,可能在电脑上使用该软件的时候时间过长导致近视眼,在选择年级为123的时候,每道题结束后都会显示目前为止所花的时间,当时间达到设定值的时候会提示并结束本次答题。
如上图所示,设定时间限制5000ms,输入3题,但实际完成两题后时间超过5000ms,此时控制台显示用时过长,休息一下吧!
4.3增加需求:优化题目难度(责任人:郑会吉)
对于小学一年级的题目,只有加减操作,但可能会出现结果为负数的情况,考虑到小学可能没有学习负数相关知识。增加新的需求,对题目难度进行优化,对于小学一年级的学生,不应该出现计算结果为负数的题,因此判断减法中如果出现a-b且a小于b的情况,就将a、b对调,从而保证减法运算的结果一定为正数。修改部分代码如下所示:
switch (mode) //确定运算符{case 0:c = a1 + a2;cout << a1 << "+" << a2 << "=" << endl;// return "";break;case 1:if (a1 < a2){int b1=0;b1 = a1;a1 = a2;a2 = b1;c = a1 - a2;}cout << a1 << "-" << a2 << "=" << endl;break;}
4.4增加需求:限制输出结果(责任人:刘奇)
为限制一年级计算的输出结果,在任何情况下计算的结果都不会超过对应难度的范围:例如100以内的加减不超过100,1000以内的加减不超过1000.且减法以及得到了限制,通过在加法中根据第一个生成的数限制第二个数的rand范围来实现。修改主要代码部分如下所示:
int N2=1;a1 = rand() % N;N2 = N - a1;a2 = rand() % N2;mode = rand() % 2;
4.5补充根据题目难度打分功能(责任人:郑会吉)
通过判断计算数的大小范围,100以内2分,1000以内5分,10000以内8分,由此得到卷面总分,然后计算回答正确的题目的分数,由此计算得分的比例*100%。
相关代码如下:
if (k == c) {cout << "恭喜你答对了,请再接再厉" << endl;count = count + 1;if (N == 100) {score = score + 5;}else if (N == 1000) {score = score + 8;}else {score = score + 10;}}else if (k != c) {cout << "虽然答错了,但不要灰心哦,正确答案是" << c << endl;}now_time = clock() - start;cout << "当前总用时" << now_time << endl;if (now_time > 50000){cout << "用时过长,休息一下吧!" << endl;break;}}correctrate = (count * 100) / numberExercise;finish = clock();duration = (double)(finish - start) / CLOCKS_PER_SEC;cout << "恭喜你做完了" << numberExercise << "道题目,做对了" << count << "道题目,正确率为 " << correctrate << "%,得分为" << score/score_all*100 <<" 一共耗时(s):" << duration << endl;break;
五、软件测试:
5.1 测试分析
测试前首先对测试用例进行分析:
编号 | 被测模块 | 测试功能 | 测试用例 | 期望 |
DIF-1 | Difficulty函数 | 正常输入时的输出 | 输入 1 | 100 |
DIF-2 | Difficulty函数 | 边界输入的输出 | 输入 3 | 10000 |
DIF-3 | Difficulty函数 | 错误输入的输出 | 输入 5 | 0 |
GRA-1 | Grade函数 | 低年级是否有时长限制 | 输入(1,1,5)模拟时长6000 | "用时过长,休息一下吧!" |
GRA-2 | Grade函数 | 1年级是否不会输出结果为负数的题 | 输入(1,1,)模拟随机数2-100 | 输出交换两数位置 |
GRA-3 | Grade函数 | 1年级是否不会输出结果超过操作数大小的题 | 输入(1,1,20) | 20个题结果均在100以内 |
GRA-4 | Grade函数 | 输入6年级边界测试 | 输入(6,3,100) | |
GRA-5 | Grade函数 | 输入1年级边界测试 | 输入(1,1,100) | |
GRA-6 | Grade函数 | 4年级是不是4个操作数 | 输入(4,1,100) | 检查100个题目满足 |
GRA-7 | Grade函数 | 题目是不是不会重复 | 输入(1,1,100) | 检查100个题目不重复 |
5.2测试代码
如下:
#include "pch.h"
#include "CppUnitTest.h"
#include "../main/四则运算.cpp"using namespace Microsoft::VisualStudio::CppUnitTestFramework;class MockInput {
public:MockInput(int inputValue) : m_inputValue(inputValue) {}int getInput() {return m_inputValue;}private:int m_inputValue;
};namespace 四则test
{TEST_CLASS(四则test){public:int expected1 = 100; TEST_METHOD(TestMethod1){int difficulty = 1;int number_max = Difficulty(difficulty);Assert::AreEqual(expected1, number_max);}TEST_METHOD(TestMethod2){int difficulty = 4;int expected2 = 0;std::string err_diff = "请输入正确的难度。";//string warn_err_diff = Difficulty(difficulty);int number_max = Difficulty(difficulty);Assert::AreEqual(expected2, number_max);}/*TEST_METHOD(std_test){int difficulty = 4;int expected2 = 0;std::string err_diff = "请输入正确的难度。";//string warn_err_diff = Difficulty(difficulty);int number_max = Difficulty(difficulty);std::stringstream buffer;std::streambuf* sbuf = std::cout.rdbuf(); // Save cout's bufferstd::cout.rdbuf(sbuf);std::cout << "std original buffer: \n";std::cout << buffer.get();Assert::AreEqual(err_diff, buffer.str());}*/TEST_METHOD(Testgrade1){int grade = 1;int test_num = 1;int max_num = 100;int expected2 = 0;std::string expected = "Hello World!\n";//string warn_err_diff = Difficulty(difficulty);Grade(grade, test_num, max_num);MockInput input(5);Grade(input.getInput());std::stringstream buffer;std::streambuf* sbuf = std::cout.rdbuf(); // Save cout's bufferstd::cout.rdbuf(sbuf);std::cout << "std original buffer: \n";std::cout << buffer.get();Assert::AreEqual(expected, buffer.str());}};
}
目前仅对Difficulty函数进行了测试,Grade函数返回值为空,且需要模拟控制台输入,比较复杂,目前测试未通过;(有待下一步深入学习)
六、学习记录
6.1 每日学习日志
学习时段 | 学习内容 | 收获体会 | 效率评价 | |
1016 | 2000-2200 | 需求分析 | 要明确需求的谁对谁的需求,不要把需求写成了技术实现 | 好 |
1017 | 1400-1800 | 编写初代代码 | 从开源代码中学习了不少好的思路 | 好 |
1018 | 1400-1730 | 初代代码运行与效能分析 | 效能分析的结果许多不一定是语言的问题,而是与系统执行有关 | 良好 |
1019 | 1400-1800 | Gitee高级操作 | 协同编程的时候,通过更新自己的分支到最新再pullreqeuest否则会冲突 | 一般 |
1020 | 0800-1400 | 软件测试,返回值为void的函数如何进行测试,需要交互后运行的函数如何通过mock来模拟控制台输入 | 软件=程序+数据+文档 测试的目的:需求规格说明与实际结果是否一致 | 一般 |
1021 | ||||
学习时间 | 学习内容 | 收获体会 | 自我效率评价 |
2023/10/12 | 计划和预估 | 两人商量确定了项目的计划和流程 | 基本达到预期目标 |
1400-1600 | |||
2023/10/13 | 需求分析 | 找相关资料,完成开发前的准备工作 | 基本达到预期目标 |
0800-1100 | |||
2023/10/13 | 开发 | 编码,实现题目的生成 | 未达到预期目标 |
1400-1700 | |||
1900-2300 | |||
2023/10/14 | 开发 | 编码,实现题目的生成、答题功能并验证答案是否正确、判分,并对历史成绩进行存储和查询。 题目避免重复 | 基本达到预期目标 |
0800-1100 | |||
1400-1700 | |||
1900-2300 | |||
2023/10/18 | 开发和测试 | 代码复审、单元测试 | 基本达到预期目标 |
1400-1700 | |||
1900-2300 | |||
2023/10/19 | 测试和总结 | 文档撰写 | 基本达到预期目标 |
1400-1700 | |||
1900-2300 |
6.2 PSP效率分析:
根据个人软件开发的几个主要步骤,给出了预估耗时以及实际耗时的对比,其中代码以及测试方面所花时间远超过预估,主要是因为调试不是很熟练,花的时间比较久。整体而言,整个开发流程的实际耗时比预估多了69%,一方面是经验不足,一方面也是时间比较碎,每次进行相关工作需要先进入状态才能开始高效率的工作。
个人软件开发阶段 | 预估耗时 | 实际耗时 |
计划 | 2h | 2h |
需求分析,生成设计文档 | 1h | 2h |
代码规范审查 | 1h | 1h |
具体编码 | 5h | 10h |
测试 | 2h | 6h |
测试报告 | 3h | 1h |
总结 | 2h | 5h |
合计 | 16h | 27h |
这篇关于基于四则运算项目的结对编程项目作业记录的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!