第三周作业
李兆希(3124004099)https://github.com/LZX2025/SoftwareEngineeringWork/tree/main/Week3
杨旭东(3124004109)
代码组织
项目分为三个文件:
fraction_utils.py 分数格式化、词法分析、表达式解析
generator.py 表达式树、随机生成、去重、转字符串、批量生成
Myapp.py 命令行参数解析、生成模式、判分模式、文件读写
关键代码
表达式生成与约束检查
def gen_expr(max_ops: int, r: int, depth: int = 0) -> Node:
if max_ops == 0:
return Node(val=random_leaf(r))
left_ops = random.randint(0, max_ops - 1)
right_ops = max_ops - 1 - left_ops
left = gen_expr(left_ops, r, depth + 1)
right = gen_expr(right_ops, r, depth + 1)
ops = ["+", "−", "×", "÷"]
random.shuffle(ops)
for op in ops:
if op == "+":
return Node(op, left, right, left.val + right.val)
elif op == "−":
if left.val >= right.val:
return Node(op, left, right, left.val - right.val)
elif op == "×":
return Node(op, left, right, left.val * right.val)
elif op == "÷":
if right.val != 0 and 0 < left.val < right.val:
return Node(op, left, right, left.val / right.val)
raise ValueError("无法生成满足约束的表达式")
表达式转字符串
def expr_to_string(node: Node) -> str:
if node.op is None:
return format_fraction(node.val)
left_str = expr_to_string(node.left)
right_str = expr_to_string(node.right)
if node.left.op is not None and precedence(node.left.op) < precedence(node.op):
left_str = f"({left_str})"
if node.right.op is not None:
rp = precedence(node.right.op)
cp = precedence(node.op)
if rp < cp:
right_str = f"({right_str})"
elif rp == cp:
if not ((node.op == "+" and node.right.op == "+") or
(node.op == "×" and node.right.op == "×")):
right_str = f"({right_str})"
return f"{left_str} {node.op} {right_str}"
性能分析
已生成 1000 道题目:Exercises.txt, Answers.txt
205277 function calls (192542 primitive calls) in 0.074 seconds
Ordered by: internal time
ncalls tottime percall cumtime percall filename:lineno(function)
5028/1000 0.006 0.000 0.032 0.000 generator.py:42(gen_expr)
14248 0.005 0.000 0.008 0.000 random.py:245(_randbelow_with_getrandbits)
8206 0.003 0.000 0.009 0.000 random.py:335(randint)
4014 0.003 0.000 0.004 0.000 fraction_utils.py:4(format_fraction)
124 0.003 0.000 0.003 0.000 {built-in method nt.stat}
24/1 0.003 0.000 0.074 0.074 {built-in method builtins.exec}
3014 0.003 0.000 0.011 0.000 generator.py:16(random_leaf)
5028/1000 0.002 0.000 0.004 0.000 generator.py:83(norm)
3 0.002 0.001 0.002 0.001 {built-in method _imp.create_dynamic}
1 0.002 0.002 0.046 0.046 generator.py:133(generate)
5028/1000 0.002 0.000 0.005 0.000 generator.py:107(expr_to_string)
...
从 cProfile 结果看,消耗最大的函数是:
generator.gen_expr:递归生成表达式树,需要反复尝试满足减法非负、除法为真分数等约束。
改进思路
减少无效尝试:在 gen_expr 中提前判断运算符是否满足约束,不满足则直接换下一个运算符,避免大量异常。
测试用例
| 编号 | 测试内容 | 输入/操作 | 预期结果 |
|---|---|---|---|
| 1 | 生成 10 道 10 以内题目 | python Myapp.py -n 10 -r 10 | 生成 Exercises.txt 和 Answers.txt,各 10 行 |
| 2 | 缺少 -r 参数 | python Myapp.py -n 5 | 报错并提示帮助信息 |
| 3 | -r 1 边界 | python Myapp.py -n 5 -r 1 | 生成 5 道题,数值均为 0 |
| 4 | 运算符个数不超过 3 | 检查生成的题目 | 每行运算符 ≤ 3 |
| 5 | 减法不出现负数 | 检查生成题目及计算过程 | 所有减法结果 ≥ 0 |
| 6 | 除法结果为真分数 | 检查生成题目 | 除法结果 0 < 结果 < 1 |
| 7 | 题目不重复 | 生成 100 道题,检查去重 | 无重复等价题目 |
| 8 | 分数格式 | 检查含带分数的题目 | 格式如 2’3/8 |
| 9 | 判分模式正确统计 | 构造 TestEx.txt 与 TestAns.txt,运行 python Myapp.py -e TestEx.txt -a TestAns.txt | Grade.txt 输出 Correct/Wrong 及题号 |
| 10 | 判分模式错题识别 | 故意写错部分答案 | 错题题号出现在 Wrong 中 |
PSP
| PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| Planning | 计划 | ||
| · Estimate | · 估计这个任务需要多少时间 | 5 | 5 |
| Development | 开发 | ||
| · Analysis | · 需求分析(包括学习新技术) | 10 | 15 |
| · Design Spec | · 生成设计文档 | 20 | 25 |
| · Design Review | · 设计复审(和同事审核设计文档) | 10 | 5 |
| · Coding Standard | · 代码规范(为目前的开发制定合适的规范) | 5 | 5 |
| · Design | · 具体设计 | 20 | 30 |
| · Coding | · 具体编码 | 120 | 120 |
| · Code Review | · 代码复审 | 10 | 20 |
| · Test | · 测试(自我测试,修改代码,提交修改) | 10 | 15 |
| Reporting | 报告 | ||
| · Test Report | · 测试报告 | 30 | 40 |
| · Size Measurement | · 计算工作量 | 10 | 10 |
| · Postmortem & Process Improvement Plan | · 事后总结,并提出过程改进计划 | 5 | 5 |
| 合计 |

浙公网安备 33010602011771号