结对作业
| 这个作业属于哪个课程 | https://edu.cnblogs.com/campus/gdgy/Class34Grade23ComputerScience |
|---|---|
| 这个作业要求在哪里 | https://edu.cnblogs.com/campus/gdgy/Class34Grade23ComputerScience/homework/13479 |
| 这个作业的目标 | 实现一个自动生成小学四则运算题目的程序;体验结对完成项目的过程并进行反思总结 |
| github链接 | https://github.com/yasinalong/Calculation-problem-generation.git |
| 成员一 | 黄昌龙 3123004077 |
| 成员二 | 刘江浩 3123004752 |
一、PSP表格
| PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| Planning | 计划 | 15 | 20 |
| · Estimate | · 估计这个任务需要多少时间 | 1000 | 1063 |
| Development | 开发 | 240 | 260 |
| · Analysis | · 需求分析 (包括学习新技术) | 40 | 50 |
| · Design Spec | · 生成设计文档 | 20 | 30 |
| · Design Review | · 设计复审 (和同事审核设计文档) | 20 | 15 |
| Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 3 | 3 |
| · Design | · 具体设计 | 30 | 40 |
| · Coding | · 具体编码 | 240 | 260 |
| · Code Review | · 代码复审 | 50 | 40 |
| · Test | · 测试(自我测试,修改代码,提交修改) | 60 | 100 |
| Reporting | 报告 | 180 | 190 |
| · Test Report | · 测试报告 | 20 | 20 |
| · Size Measurement | · 计算工作量 | 3 | 5 |
| · Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 20 | 30 |
| 合计 | 941 | 1063 |
二、效能分析

文件I/O是最大瓶颈(32.68%)
字符串操作开销大(9.87%)
问题生成逻辑(8.35%)
主要还是文件 IO 的性能
三、设计实现过程
1. 核心类设计
程序包含4个核心类,分别负责不同的功能模块。
- Fraction 类用于处理分数的表示、运算和格式化,其属性包括 numerator(分子)和 denominator(分母),方法有四则运算重载、toString ()(分数格式化)以及 reduce ()(约分)。
- ExpressionNode 类是表达式树的节点,用于构建抽象语法树(AST),属性包括 type(节点类型:数字 / 运算符)、value(分数值)、op(运算符)以及 left/right(子节点)。
- Expression 类负责管理表达式树的解析、求值、规范化和字符串转换,方法有 evaluate ()(递归求值)、normalize ()(表达式去重规范化)和 toString ()(表达式格式化)。
- ProblemGenerator 类用于生成不重复的四则运算题目,属性包括 range(数值范围)和 generatedExpressions(去重集合),方法有 generateProblem ()(生成题目)和 generateExpressionTree ()(生成表达式树)。
- AnswerChecker 是校验用户答案与标准答案一致性的静态类,其静态方法 checkAnswers () 用于对比题目文件和答案文件,并输出正确率。
2. 类之间的关系
- 依赖关系:
Expression 依赖 ExpressionNode(通过指针管理表达式树节点)。
ExpressionNode 依赖 Fraction(存储数字节点的数值)。
ProblemGenerator 依赖 Expression 和 ExpressionNode(生成表达式并封装为 Expression 对象)。
AnswerChecker 依赖 Expression(解析题目表达式并计算标准答案)。 - 组合关系:
Expression 包含 ExpressionNode 指针(root),表示一个完整的表达式树。
3. 关键函数流程分析
- 表达式生成流程(ProblemGenerator::generateProblem())
![image]()
- 表达式解析与求值流程(Expression::Expression(const string&) 和 evaluate())
![image]()
- 答案校验流程(AnswerChecker::checkAnswers())
![image]()
四、代码说明
在源文件的头文件中注释的非常的清晰了
最重要的部分就是表达式生成类了

这个类实现了,表达式的存储,存储表达树的数据结构是树
五、测试运行
- 测试分数类(是不可以创造分母为0的数的,如果创造了分母为0也是默认为1)
ftest.cpp
#include "head.h"
int main()
{
Fraction a(1,3),b(5,2),c("1/3"),d("2'2/3"),e("3");
std::cout << a.toString()<< std::endl;
std::cout << b.toString()<< std::endl;
std::cout << c.toString()<< std::endl;
std::cout << d.toString()<< std::endl;
std::cout << e.toString()<< std::endl;
return 0;
}

2. 测试表达式函数
表达式类测试
#include"head.h"
using namespace std;
int main()
{
ProblemGenerator h(10);
for(int i=0;i<10;i++)
{
auto [e,f] = h.generateProblem();
std::cout << e.toString() << "=" << f.toString() <<std::endl;
}
return 0;
}

expression函数功能测试
int main()
{
/*创建一棵树*/
/*
root
root1 roo2
leaf1 leaf2 leaf3 leaf4
*/
auto leaf1 = make_shared<ExpressionNode> (Fraction(1));
auto leaf2 = make_shared<ExpressionNode> (Fraction(2));
auto root1 = make_shared<ExpressionNode> ('+',leaf1,leaf2);
auto leaf3 = make_shared<ExpressionNode> (Fraction(3));
auto leaf4 = make_shared<ExpressionNode> (Fraction(4));
auto root2 = make_shared<ExpressionNode> ('+',leaf3,leaf4);
auto root = make_shared<ExpressionNode>('+',root1,root2);
Expression e(root);
std::cout << "结果:" << e.evaluate().toString() << std::endl;
std::cout << "表达式(带括号):" << e.toString() << std::endl;
std::cout << "表达式(不带括号:" << e.toNormalizedString() << std::endl;
return 0;
}

3. 测试3
测试去重功能

class ProblemGeneratorText
{
public:
void printExrHash(ProblemGenerator& obj,Expression exr)
{
std::cout<<"表达式(未整理前):"<< exr.toString()<<std::endl;
exr.normalize();
std::cout<<"表达式(整理后):"<<exr.toString()<<std::endl;
std::string str = exr.toNormalizedString();
if(obj.generatedExpressions.find(str) != obj.generatedExpressions.end())//找得到
std::cout<<"已经存在了这个表达式"<<std::endl;
else
{
std::cout<<"加入表达式:"<<std::endl;
obj.generatedExpressions.insert(str);
}
}
};
int main()
{
/*创建一棵树*/
/*
root
root1 leaf3
leaf1 leaf2
*/
auto leaf1F = make_shared<ExpressionNode> (Fraction(1));
auto leaf2F = make_shared<ExpressionNode> (Fraction(2));
auto root1F = make_shared<ExpressionNode> ('+',leaf1F,leaf2F);
auto leaf3F = make_shared<ExpressionNode> (Fraction(3));
auto rootF = make_shared<ExpressionNode>('+',root1F,leaf3F);
Expression e1(rootF);
auto leaf1L = make_shared<ExpressionNode> (Fraction(2));
auto leaf2L = make_shared<ExpressionNode> (Fraction(1));
auto root1L = make_shared<ExpressionNode> ('+',leaf1L,leaf2L);
auto leaf3L = make_shared<ExpressionNode> (Fraction(3));
auto rootL = make_shared<ExpressionNode>('+',root1L,leaf3F);
Expression e2(rootL);
ProblemGeneratorText t;
ProblemGenerator h(10);
t.printExrHash(h,e1);
t.printExrHash(h,e2);
return 0;
}


六、性能优化
1.优化 io
优化思路,减少文件io

但是效果其实并不明显
优化前

优化后

基本上差异来源于波动,因此根本就没有必要在文件io上苦费时间了
因为这个时候操作系统和库函数已经对文件io进行了优化,设置了缓冲区,但是确实性能开销中文件 io 是消耗最多性能的
2.编译优化
性能直接提升了3倍多(在100000道题目的前提下)

3.多线程优化
由于现在的性能已经可以足够了,所以就没有实现多线程优化,因为这样会是程序更加复杂,但是这里也是抛出优化的方案
七、项目小结
本次结对项目不仅完成了功能开发,更让我们体会到协作的价值:通过优势互补提高代码质量,通过思想碰撞解决技术难题。未来项目中,我们将保留每日同步、代码评审等协作机制,同时改进异常处理和性能优化,让程序更健壮、更易用。结对开发不是简单的任务分配,而是共同对项目负责——这种责任感,正是团队开发的核心意义。




浙公网安备 33010602011771号