这个作业属于哪个课程 | 计科1/2班 |
---|---|
这个作业要求在哪里 | 结对项目 |
这个作业的目标 | 自动生成小学四则运算题目 |
团队成员
姓名 | 学号 |
---|---|
潘俊羽 | 3121005138 |
罗寰宇 | 3121005137 |
Github作业链接:SE-PairProgramming
PSP表
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | ||
· Estimate | · 估计这个任务需要多少时间 | 120 | 200 |
Development | 开发 | ||
· Analysis | · 需求分析 (包括学习新技术) | 180 | 120 |
· Design Spec | · 生成设计文档 | 60 | 90 |
· Design Review | · 设计复审 (和同事审核设计文档) | 30 | 120 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 30 | 30 |
· Design | · 具体设计 | 120 | 150 |
· Coding | · 具体编码 | 240 | 520 |
· Code Review | · 代码复审 | 60 | 120 |
· Test | · 测试(自我测试,修改代码,提交修改) | 60 | 90 |
Reporting | 报告 | ||
· Test Report | · 测试报告 | 60 | 30 |
· Size Measurement | · 计算工作量 | 60 | 30 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 60 | 30 |
合计 | 1080 | 1530 |
一、需求分析
- 生成10以内四则运算题目,可通过树结构正则表达式实现。
- 计算过程中不能出现负数,生成正则表达式过程中若树结构某一节点
result
为负,则调换左右子树。 - 计算过程不能出现假分数,通过
Fraction
函数将假分数用带分数表示。 - 运算符数量不能超过3个,可通过控制树的深度实现。
- 任何两道题目不能通过有限次交换变换为同一道题目,可对比两颗树实现。若两表达式的左子树等于左子树且右子树等于右子树或左子树等于右子树且右子树等于左子树,即判定两树相似,需要重新生成新的表达式。
二、设计文档
2.1 参数控制
程序支持如下参数的给定
-n <num>
控制题目生成个数-r <num>
控制题目中的数值范围,例如输入10则数值范围为0~9-e <exercisefile>.txt -a <answerfile>.txt
根据给定的exercisefile
和answerfile
判定题目
需要对参数获取并进行功能选取
2.2 分数表示类
提供分数的构造和运算
构造函数声明
// 默认构造函数,为0
Fraction();
// 构造整数,分母为1
Fraction(int num);
// 传入整型的分子和分母构造分数
Fraction(int num, int den);
// 传入分数型的分子和分母构造分数
Fraction(Fraction fNum, Fraction fDen);
运算符重载
// 流输入输出
friend ostream& operator<<(ostream& os, const Fraction& f);
friend istream& operator>>(istream& is, Fraction& f);
// 运算
friend Fraction operator+(const Fraction& f1, const Fraction& f2);
friend Fraction operator-(const Fraction& f1, const Fraction& f2);
friend Fraction operator*(const Fraction& f1, const Fraction& f2);
friend Fraction operator/(const Fraction& f1, const Fraction& f2);
friend Fraction operator+=(Fraction& f1, const Fraction& f2);
friend Fraction operator-=(Fraction& f1, const Fraction& f2);
friend Fraction operator*=(Fraction& f1, const Fraction& f2);
friend Fraction operator/=(Fraction& f1, const Fraction& f2);
friend Fraction operator-(const Fraction& f);
friend Fraction operator++(Fraction& f);
friend Fraction operator++(Fraction& f, int);
friend Fraction operator--(Fraction& f);
friend Fraction operator--(Fraction& f, int);
// 比较
friend bool operator>(const Fraction& f1, const Fraction& f2);
friend bool operator<(const Fraction& f1, const Fraction& f2);
friend bool operator>=(const Fraction& f1, const Fraction& f2);
friend bool operator<=(const Fraction& f1, const Fraction& f2);
friend bool operator==(const Fraction& f1, const Fraction& f2);
friend bool operator!=(const Fraction& f1, const Fraction& f2);
算法:获取最大公约数
static int MCF(int a, int b);
2.3 表达式类
提供如下公开接口
// 获得字符串
std::string GetString() const;
// 获得表达式结果
std::Fraction GetResult() const;
表达式内部使用树结构存储,定义如下:
struct TreeNode {
string val;
Fraction result;
TreeNode* left;
TreeNode* right;
TreeNode(string x) : val(x), result(0), left(nullptr), right(nullptr) {}
TreeNode(Fraction x) : val(" "), result(0), left(nullptr), right(nullptr) {
stringstream ss;
ss << x;
val = ss.str();
}
};
使用二叉树存储表达式,以此实现最多三个运算符的表达式生成和存储
2.4 题目生成模块
// 生成表达式功能函数主体
void GenerateExpression(const int exerciseCount, const int maxRandomNum);
流程图
2.5 题目判定模块
// 判断答案函数主体
void JudgeAnswers(const std::string& exerciseFileName, const std::string& answersFileName);
流程图
2.6 文件IO模块
将生成的题目列表写入文件 Exercises.txt
,将对应的答案列表写入文件 Answers.txt
读取 <exercisefile>.txt
中的题目文件,读取 <answerfile>.txt
中的答案文件
文件格式如下
题目文件
1.题目1
2.题目2
...
答案文件
1.答案1
2.答案2
...
提供四个函数执行相关操作
// 向文件写入习题列表,传入参数:习题列表
void WriteExercisesToFile(const std::vector<Expression>& exercises);
// 向文件写入答案列表,传入参数:习题列表
void WriteAnswersToFile(const std::vector<Fraction>& results);
// 从文件读取习题列表,传入参数:文件名称、习题列表引用
void ReadExercisesFromFile(const string fileName, std::vector<Expression>& outExpressions);
// 从文件读取答案列表,传入参数:文件名称、习题列表引用
void ReadAnswersFromFile(const string fileName, std::vector<Fraction>& outFraction);
// 写入判定结果文件
void WriteJudgeResultToFile(std::vector<int>& correctList,std::vector<int>& wrongList);
2.7 报错提示模块
所有的错误信息使用 cerr
输出
对参数 -n <num>
和 -r <num>
需要保证 <num> > 0
对参数 -e
和 -a
需要同时接收并且检查 <exercisefile>.txt
和 <answerfile>.txt
能否正常读取
不允许两种功能的参数混用
三、测试报告
3.1 性能测试
生成表达式的查重较为消耗时间性能,每生成一次表达式遍历所有表达式以避免重复。修改后使用C++标准库提供的map类型,遍历结果相同的表达式以节省性能。
3.2 结果测试
测试表达式如下
1.( 0 × 9 ) ÷ ( 18 + 18 )
2.5 + 5
3.( 15 - 11 ) ÷ ( 7 × 16 )
4.13 × 12 + 1 × 16
5.7 ÷ 6
6.( 12 - 7 ) × 15 ÷ 14
7.( 13 × 13 ) ÷ ( 1 + 11 )
8.4 ÷ 2 + 19 - 17
9.( 10 ÷ 2 ) - ( 18 - 16 )
10.0 × 2
计算结果如下
1.0
2.10
3.1/28
4.172
5.1'1/6
6.5'5/14
7.14'1/12
8.4
9.3
10.0
共计生成10000条表达式,限于篇幅,展示其中10条,其表达式和结果分别保存在Exercise.txt
和Answers.txt
文件中。
四、Code Standard
借鉴微软和Unity风格
4.1 命名
每种成员后方即为标准命名(命名样式名称和预览),中括号扩起的部分为可选命名方案
- 类型和名称空间
UpperCamelCase
[UpperCamelCase_UnderscoreTolerant
] - 类型参数(模板)
TUpperCamelCase
- 函数(成员)
UpperCamelCase
- 本地变量(函数体内)
lowerCamelCase
- 本地常量(函数体内)
lowerCamelCase
- 方法参数(函数体内)
lowerCamelCase
- 公有实例字段(成员)
UpperCamelCase
[UpperCamelCase_UnderscoreTolerant
] - 私有实例字段(成员)
_lowerCamelCase
[_lowerCamelCase_underscoreTolerant
] - 公有静态字段(成员)
UpperCamelCase
[UpperCamelCase_UnderscoreTolerant
] - 私有静态字段(成员)
_lowerCamelCase
[_lowerCamelCase_underscoreTolerant
] - 常量字段(成员)
UpperCamelCase
[UpperCamelCase_UnderscoreTolerant
] - 枚举成员
UpperCamelCase
- 本地方法
UpperCamelCase
- 其他实体
UpperCamelCase
4.2 Git-Commit
-
feat: 新功能
-
fix: 修复bug
-
docs: 文档
-
refactor: 代码重构
-
test: 测试用例
-
chore: 其他修改,例如构建流程、依赖管理
-
scope: commit影响的范围,例如Utils, Build...
-
subject: commit的概述,建议符合50/72 formatting
-
body: commit具体修改内容,可以分为多行,建议符合50/72 formatting
-
footer: 一些备注,通常是 BREAKING CHANGE 或修复bug的链接
五、项目小结
通过本次项目,我们对于团队合作开发项目有了初步的了解。对于需要多人合作的项目应当明确需求,制定好完整的项目框架,将需求拆分成各个模块。设计好功能模块后将具体任务分配给个人,两个人各司其职,以保证任务顺利推进。与此同时,还应当充分交流,当项目遇到问题应当集思广益,避免因某个模块的问题而影响整个任务进度。