结对项目
软件工程第二周作业
| 这个作业属于哪个课程 | 软件工程 |
|---|---|
| 这个作业要求在哪里 | 作业要求 |
| 这个作业的目标 | 实现一个自动生成小学四则运算题目的命令行程序 |
| 作业GitHub网址 | https://github.com/ww7c/3124001541_project2 |
| 巫庆城 | 3124001541 |
一、psp表格
| PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| Planning | 计划 | 20 | 30 |
| Estimate | · 估计这个任务需要多少时间 | 20 | 30 |
| Development | 开发 | 480 | 540 |
| Analysis | · 需求分析 (包括学习新技术) | 60 | 60 |
| Design Spec | · 生成设计文档 | 30 | 30 |
| Design Review | · 设计复审 | 30 | 60 |
| Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 30 | 20 |
| Design | · 具体设计 | 60 | 60 |
| Coding | · 具体编码 | 150 | 240 |
| Code Review | · 代码复审 | 60 | 30 |
| Test | · 测试(自我测试,修改代码,提交修改) | 60 | 40 |
| Reporting | 报告 | 90 | 120 |
| Test Report | · 测试报告 | 30 | 50 |
| Size Measurement | · 计算工作量 | 30 | 30 |
| Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 30 | 40 |
| ·合计 | 590 | 690 |
二、效能分析
测试调用:Project2.exe -n 10000 -r 100
性能分析图由VS 2022的性能分析工具自动生成:

| 函数 | CPU 总计 | 自 CPU |
|---|---|---|
main |
53(89.83%) | 0 |
random_expression::getExpression |
7(11.86%) | 0 |
random_Fraction::tofraction |
4(6.78%) | 1.69% |
random_num::getnumber |
2(3.39%) | 0 |
random_expression::getExpression() 的 CPU 总计为 11.86%,是目前显示的用户函数中 CPU 占用最高的。但getExpression() 本身没有明显耗时,但它调用的函数消耗了 CPU。故可优化其调用的函数random_Fraction::tofraction / random_num::getnumber 。
优化一:减少 getExpression() 中无意义的转换
Fraction prev_value = random_Fraction::tofraction(nums1);
//初始化运算结果为0
Fraction result (0,1);
//中间存储变量
Fraction temp = prev_value;
//改为
Fraction temp = random_Fraction::tofraction(nums1);
优化二:getnumber()中把随机引擎改成 static:
int random_num::getnumber(int max)
{
random_device rd;
mt19937 gen(rd());
uniform_int_distribution<> dis(0, max - 1);
return dis(gen);
}
改为
int random_num::getnumber(int max)
{
static random_device rd;
static mt19937 gen(rd());
uniform_int_distribution<> dis(0, max - 1);
return dis(gen);
}
优化三:程序运行过程中控制台输出占用了较多 CPU 时间,性能分析结果中 std::endl 等输出相关函数占比较高。因此首先减少不必要的调试输出。
优化后:
运行时间:6.306 s
getExpression:10.24%
tofraction:4.37%
getnumber:2.00%
三、代码设计实现过程
| 类/模块 | 主要功能 | 主要函数 |
|---|---|---|
input_parameters |
输入参数 | void input() |
random_num |
生成随机整数 | getnumber(int max) |
random_operator |
随机生成四则运算符 | getoperator() |
Fraction |
表示和处理分数 | +、-、*、/、比较、约分等 |
random_Fraction |
生成随机分数及进行字符串与分数转换 | getFraction(int max)、tofraction(string) |
random_expression |
生成完整算术表达式并计算结果 | getExpression(int max) |
check |
对用户答案进行批改 | 答案读取、比较、统计 |
模块关系
input_parameter
↓
random_num
↓
random_operator
↓
random_expression
↓
计算正确答案
↓
exercisefile.txt ─────→ 用户填写答案 ─────→ user_answerfile.txt
│ ↓
└→ proper_answerfile.txt ─────────→ 答案批改
↓
Grade.txt
四、代码说明
(1)表达式生成
string random_expression::getExpression(int max)
{
//随机生成运算符数量,范围为1-3
int operator_count = random_num::getnumber(3) + 1;
//生成第一个数
string nums1 = random_Fraction::getFraction(max);
Fraction prev_value = random_Fraction::tofraction(nums1);
//初始化运算结果为0
Fraction result (0,1);
//中间存储变量
Fraction temp = prev_value;
//表达式变量
string expression = nums1;
//存储变量前的运算符号
string prev_op = "+";
for(int i=0;i<operator_count;i++)
{
string next_op = random_operator::getoperator();//随机生成运算符
//生成下一个数
string nums2= random_Fraction::getFraction(max);
Fraction next_value = random_Fraction::tofraction(nums2);
//后面符号判定代码
}
getExpression() 是程序的核心函数。首先调用random_num、random_Fraction和random_operator模块生成表达式中的各个部分,然后使用 tofraction 函数转为fraction型的数据,再进行相应的计算。
(2)重构运算符
//重构运算符
//分数加法
Fraction Fraction::operator+(const Fraction& other) const {
int new_numerator = numerator * other.denominator + other.numerator * denominator;
int new_denominator = denominator * other.denominator;
Fraction result(new_numerator, new_denominator);
return result;
}
//分数减法
Fraction Fraction::operator-(const Fraction& other) const {
int new_numerator = numerator * other.denominator - other.numerator * denominator;
int new_denominator = denominator * other.denominator;
Fraction result(new_numerator, new_denominator);
return result;
}
//分数乘法
Fraction Fraction::operator*(const Fraction& other) const {
int new_numerator = numerator * other.numerator;
int new_denominator = denominator * other.denominator;
Fraction result(new_numerator, new_denominator);
return result;
}
//分数除法
Fraction Fraction::operator/(const Fraction& other) const {
int new_numerator = numerator * other.denominator;
int new_denominator = denominator * other.numerator;
Fraction result(new_numerator, new_denominator);
return result;
}
//分数比较
bool Fraction::operator<(const Fraction& other) const {
return numerator * other.denominator < denominator * other.numerator;
}
bool Fraction::operator==(const Fraction& other)const {
return numerator * other.denominator == denominator * other.numerator;
}
对fraction型数据的运算符进行重构,用于后面运算符优先性判定、结果计算。
(3)答案批改
// 比较两个 Fraction
if (proper == user)
{
// 答案正确
correctCount++;
if (!correctQuestions.empty())
{
correctQuestions += ", ";
}
correctQuestions += to_string(questionNumber);
}
else
{
// 答案错误
wrongCount++;
if (!wrongQuestions.empty())
{
wrongQuestions += ", ";
}
wrongQuestions +=to_string(questionNumber);
}
}
else
{
// 用户没有填写答案
wrongCount++;
if (!wrongQuestions.empty())
{
wrongQuestions += ", ";
}
wrongQuestions += to_string(questionNumber);
}
批改模块逐行读取标准答案和用户答案,并按照题目顺序进行比较。如果两行答案相同,则记录为正确,否则记录为错误,最后生成 Grade.txt。
五、测试运行
| 编号 | 测试内容 | 输入/操作 | 预期结果 |
|---|---|---|---|
| 1 | 生成少量题目 | Myapp.exe -n 10 |
成功生成10道四则运算题目 |
| 2 | 生成1道题 | Myapp.exe -n 1 |
成功生成1道题目,程序正常结束 |
| 3 | 生成100道题 | Myapp.exe -n 100 |
成功生成100道题目,无程序崩溃 |
| 4 | 生成10000道题 | Myapp.exe -n 10000 |
成功生成10000道题目,满足题目数量要求 |
| 5 | 整数运算 | 随机生成包含整数的题目 | 操作数格式正确,计算结果正确 |
| 6 | 真分数运算 | 随机生成包含 1/2、2/3 等分数的题目 |
分数格式正确,计算结果正确 |
| 7 | 带分数运算 | 随机生成包含 1'1/2 等带分数的题目 |
带分数格式正确,能够正确计算 |
| 8 | 除法运算 | 随机生成包含 / 的题目 |
除数不为0,不出现除零情况 |
| 9 | 减法运算 | 随机生成包含 - 的题目 |
被减数一定大于减数 |
| 10 | 运算优先级 | 测试类似 8 - 3 × 2 - 4 的表达式 |
先计算乘除,再计算加减,运行优先级正确 |
| 11 | 批改正确答案 | proper_answerfile文件 + user_answerfile文件 | Grade.txt 中正确题数与实际一致 |
| 12 | 批改错误答案 | proper_answerfile文件 + 部分错误答案的user_answerfile文件 | 正确、错误题目分别统计,题号正确 |
| 13 | 用户答案数量不足 | user_answerfile文件少于exercise题目数 | 程序能够识别未提供答案的题目 |
| 14 | 用户答案数量过多 | user_answerfile文件多于exercise题目数 | 程序不会错误地将多余答案判为正确 |
| 15 | 参数错误 | 输入错误的命令行参数 | 程序进行参数判断,不正常执行错误功能 |
六、项目小结
本次结对项目主要完成了小学四则运算题目生成程序的设计与实现。项目采用模块化的方式进行开发,将随机数生成、运算符生成、分数处理、表达式生成以及答案检查等功能分别实现,并通过 Git 对项目代码进行版本管理。
过程难点:
在实现表达式计算过程时需考虑运算符优先性问题,这部分花了较长时间.其次在随机生成表达式时需考虑边界条件,如除数不能为0,被减数需大于减数,随机生成的题目可能重复等问题。
项目中也存在一些不足。开始编写代码时对整体结构考虑得不够充分,没有在表达式生成中实现括号()这一符号。项目实现没有用图形化界面展示出来
当然通过这次项目作业,提升我在开发过程对整体结构划分的能力,积累了完成相应的模块划分,功能实现整合这些方面的经验。

浙公网安备 33010602011771号