软件工程第三次作业-结对项目
个人项目
| 项目 | 内容 |
|---|---|
| 这个作业属于哪个课程 | [软件工程](首页 - 计科23级12班 - 广东工业大学 - 班级博客 - 博客园) |
| 这个作业要求在哪里 | [作业要求](结对项目 - 作业 - 计科23级12班 - 班级博客 - 博客园) |
| 这个作业的目标 | 训练个人项目软件开发能力,学会使用性能测试工具和实现单元测试优化程勋 |
GitHub代码仓库 :lei)
作者:唐雷3123004154 袁梓轩3123004591
PSP2表格
| PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| Planning | 计划 | 10 | 15 |
| Estimate | 估计这个任务需要多少时间 | 60 | 90 |
| Development | 开发 | 250 | 270 |
| Analysis | 需求分析 (包括学习新技术) | 30 | 40 |
| Design Spec | 生成技术文档 | 30 | 30 |
| Design Review | 设计复审 | 50 | 60 |
| Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 10 | 10 |
| Design | 具体设计 | 30 | 40 |
| Coding | 具体编码 | 150 | 250 |
| Code Review | 代码复审 | 30 | 30 |
| Test | 测试(自我测试,修改代码,提交修改) | 20 | 30 |
| Reporting | 报告 | 60 | 100 |
| Test Repor | 测试报告 | 20 | 10 |
| Size Measurement | 计算工作量 | 60 | 45 |
| Postmortem & Process Improvement Plan | 事后总结,并提出过程改进计划 | 40 | 30 |
📘 小学四则运算题目自动生成与判分系统设计报告
一、代码组织
本项目的核心功能包括:
- 随机生成四则运算表达式;
- 自动计算表达式的结果;
- 验证表达式合法性;
- 对用户答案进行判分。
类设计
Expr 类
用于构建表达式树。每个节点代表一个运算单元:
- 叶子节点存储数值(
Fraction对象); - 非叶子节点包含运算符(
+ - * /)以及左右子树。
主要方法:
eval():递归计算表达式的数值;to_string():将表达式转为可读字符串;canonical():生成规范化字符串,用于表达式去重。
函数组织
| 函数名 | 功能说明 |
|---|---|
random_leaf(r) |
生成随机整数或真分数,范围受参数 r 限制 |
generate_expression(op_count, r) |
递归生成表达式树,自动验证合法性 |
format_fraction_display(fr) |
将分数格式化为整数、真分数或带分数形式 |
generate_problems(n, r) |
批量生成表达式及对应答案,并确保不重复 |
evaluate_expression_str(expr_line) |
将字符串形式的表达式解析并求值 |
grade(exercise_file, answer_file) |
对比题目与答案文件,输出正确与错误题号 |
write_exercises_answers(problems) |
将生成的题目和答案写入文件 |
模块关系概述
Expr是核心数据结构,定义表达式树节点;generate_expression使用Expr生成随机表达式;evaluate_expression_str用于解析字符串表达式;grade调用解析函数对答案进行比对;format_fraction_display和canonical用于输出标准化表达式。
结构图如下:
random_leaf ─┐
│
generate_expression ──> Expr ──> eval / to_string / canonical
│
generate_problems ────> write_exercises_answers
│
└──> grade ──> evaluate_expression_str
二、关键函数流程图
以下是 generate_expression 的核心流程:
开始
|
v
生成随机叶子(整数或分数)
|
v
随机选择操作符(+ - * /)
|
v
组合左右子树并计算结果
|
v
验证操作是否合法:
- 减法结果非负
- 除法分母非零且结果非整数
|
v
若合法 -> 构造 Expr 节点
若非法 -> 重新生成
|
v
返回完整表达式树
结束
三、算法关键与创新点
-
表达式树结构设计
使用Expr类表示表达式,既能递归计算,又能控制打印格式,避免歧义。 -
合法性约束检查
- 减法结果不得为负数;
- 除法不得出现整数结果(保持分数特性);
- 不允许除以零;
- 所有中间结果非负。
-
去重机制(Canonical 表示)
对于交换律运算符(+、*),将子表达式规范化排序,避免生成重复题。 -
带分数与真分数输出
使用format_fraction_display将结果统一为整数、真分数或带分数,输出符合小学格式。
四、项目关键代码与解释
1️⃣ 表达式生成
def generate_expression(op_count, r, max_tries=200):
for attempt in range(max_tries):
leaves_vals = [random_leaf(r) for _ in range(op_count + 1)]
leaf_nodes = [Expr(value=v) for v in leaves_vals]
nodes = leaf_nodes[:]
for _ in range(op_count):
i = random.randint(0, len(nodes) - 2)
left = nodes[i]
right = nodes[i+1]
op = random.choice(OPS)
new_node = Expr(op=op, left=left, right=right)
# 合法性验证
aval, bval = left.eval(), right.eval()
if op == '-' and aval < bval: break
if op == '/' and (bval == 0 or aval / bval == int(aval / bval)): break
nodes[i:i+2] = [new_node]
if len(nodes) == 1:
return nodes[0]
return None
功能:递归生成带有约束的表达式树。
关键点:
- 通过多次尝试生成合法表达式;
- 使用随机数控制结构多样性;
- 确保表达式无负数、无零除。
2️⃣ 表达式求值
def evaluate_expression_str(expr_line):
expr_line = expr_line.strip()
if expr_line.endswith('='):
expr_line = expr_line[:-1].strip()
tokens = tokenize(expr_line)
rpn = shunting_yard(tokens)
return eval_rpn(rpn)
功能:将字符串表达式解析为逆波兰式(RPN),再计算结果。
关键点:
- 支持整数、真分数、带分数;
- 兼容括号与多操作符优先级;
- 使用分数计算确保精度。
五、测试用例与结果验证
以下展示部分自动生成的样例题目与对应答案(节选自 Exercises.txt 和 Answers.txt):
| 题目 | 计算结果 |
|---|---|
| 4'1/2 * (6 * 1/7) = | 3'6/7 |
| (8 + (7/9 + 8)) - 1/2 = | 16'5/18 |
| (4'8/9 * 1/2) / 2/3 = | 3'2/3 |
| 7 + 1'1/2 = | 8'1/2 |
| 6 - 5/6 = | 5'1/6 |
| 0 * 5/8 = | 0 |
| 1 + 1 = | 2 |
| 1/2 * 9 = | 4'1/2 |
| (5'1/2 - 1/4) / 8 = | 21/32 |
| 2'6/7 + 3 = | 5'6/7 |
正确性验证:
- 所有结果均由
Fraction自动约分; - 与手工计算结果对比一致;
- 判分模块可正确识别学生答案格式差异。
六、结对开发经验总结
-
鼓励试错
“发现问题比快速推进更有价值”。例如在表达式生成时,初版出现除零错误,通过约束函数逐步修正。 -
主动沟通与复盘
每次修改算法逻辑(如去重规则、混合分数生成)后,双方及时复盘输出效果。 -
任务分工清晰
一人负责表达式生成与校验,另一人负责解析与判分模块,实现高效协作。 -
调试技巧
在生成表达式树时,增加打印与断点输出树结构,帮助定位逻辑错误。

浙公网安备 33010602011771号