结对项目

第三周作业

这个作业属于哪个课程 计科24级56班(广东工业大学 - 计算机学院)软件工程
这个作业要求在哪里 https://edu.cnblogs.com/campus/gdgy/Class56-Grade2024-CS/homework/15694
这个作业的目标 实现一个自动生成小学四则运算题目的命令行程序
杨富林 3124004072
黎正阳 3124004060
Github 项目地址 https://github.com/xiersg/four-arithmetic-generator

1. PSP 表格

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 20 15
· Estimate · 估计这个任务需要多少时间 15 10
Development 开发
· Analysis · 需求分析(包括学习新技术) 40 35
· Design Spec · 生成设计文档 25 20
· Design Review · 设计复审 15 10
· Coding Standard · 代码规范 15 15
· Design · 具体设计 40 35
· Coding · 具体编码 180 160
· Code Review · 代码复审 40 30
· Test · 测试(自我测试,修改代码,提交修改) 60 50
Reporting 报告
· Test Report · 测试报告 30 25
· Size Measurement · 计算工作量 15 10
· Postmortem & Process Improvement Plan · 事后总结并提出过程改进计划 25 20
合计 520 435

开始编码前先填「预估」列;做完后再填「实际」列。

2. 效能分析

生成题目时最耗时的是「构造表达式树 + 约束校验 + 规范化去重」。第一次实现时每次生成都重新求值失败就整棵扔掉,一万道题在较小 -r 时重试次数偏高。

改进思路:

  1. 在构造减法、除法节点时就地调整左右子树,减少整树作废。
  2. 用规范化元组(而不是字符串正则)做去重,比较更快、也更符合交换律定义。
  3. 文件写入一次拼好再写,避免逐行频繁打开文件。

实测:只生成 10000 道(-r 20)约 0.9 秒;连同写成 Exercises.txt / Answers.txt1–2 秒。cProfile 显示热点在 _build_tree 随机构造与 evaluate 约束检查,文件 I/O 不是主因。

在项目根目录可自行复现:

python -m cProfile -s tottime -m arithmetic -n 10000 -r 20 --seed 1

把运行截图(函数耗时表)贴到博客「效能分析」一节。改进前后可对比「生成 10000 题的墙钟时间」。

3. 设计实现过程

程序按职责拆成 5 个模块:

flowchart LR CLI[cli.py 参数解析] --> GEN[generator.py 出题] CLI --> GRD[grader.py 批改] GEN --> EXP[expression.py 表达式树] GRD --> EXP EXP --> FR[fraction.py 分数] CLI --> GUI[gui.py 图形界面]
模块 职责
Frac 约分、带分数格式、四则运算
Num / Op 二叉树表示题目;求值时检查「中间结果非负、除法得真分数」
canonical() +× 排序左右子树,识别交换律重复
generate_exercises() 随机 1~3 个运算符,集合去重后写文件
write_grade() 解析题目求值,与答案文件逐题比较

关键流程(出题):

  1. 读入 -n-r(没有 -r 则报错退出)。
  2. 随机决定运算符个数 k∈[1,3],递归生成左右子树。
  3. 求值;失败则重试。成功则计算 canonical key,已存在则重试。
  4. Exercises.txtAnswers.txt

批改:解析 e = 表达式 → 精确分数比较 → 按题号输出 Correct/Wrong。

4. 代码说明

去重是本题最容易丢分的点。1 + 2 + 3 因左结合等于 (1+2)+3,交换后得到 3+(1+2),再交换内层得到 3+(2+1),故与 3+(2+1) 重复;但 3+2+1(3+2)+1,不能只靠交换变成 (1+2)+3

规范化代码见 arithmetic/expression.pycanonical():只对 +* 交换左右孩子,不改变结合方向。

除法约束见 evaluate():结果必须是真分数(0 < |值| < 1),否则丢弃该题。

5. 测试运行

至少 10 个用例(python -m unittest discover -s tests -v):

  1. 1/6 + 1/8 = 7/24
  2. 带分数格式 5/3 → 1'2/3,并能解析 1’1/2
  3. is_proper()1/2 真,3/2、整数、0 非真
  4. 减法过程出现负数则非法
  5. 6 ÷ 3 非法(结果不是真分数),1 ÷ 2 合法
  6. 23+4545+23 判定为重复
  7. 6×88×6 判定为重复
  8. 1+2+33+(2+1) 判定为重复
  9. 1+2+33+2+1 判定为不重复
  10. 运算符优先级:1 + 2 × 3 = 7
  11. 生成 20 道题:运算符个数 ∈[1,3],答案与求值一致
  12. 按作业示例批改:Correct: 5 (1, 3, 5, 7, 9) / Wrong: 5 (2, 4, 6, 8, 10)
  13. -r 时返回错误并打印帮助
  14. 生成后用标准答案批改,应得全对

这些用例覆盖了格式、约束、去重、命令行和批改,因此可以确定程序按作业要求工作。手工再跑一遍:

python myapp.py -n 10 -r 10
python myapp.py -e Exercises.txt -a Answers.txt

6. 项目小结

两人一起完成需求拆分、接口约定和互相 review。建议在博客里各写一段:对方的闪光点、本次合作的得失、下次如何改进(例如先写测试再写生成器、更早用 10000 题压测去重性能)。

可直接使用的句子模板:

  • 杨富林:在分数约分和去重定义上把作业原文抠得很细,避免了「看起来能算但对不上样例」的情况。
  • 黎正阳:把模块边界划清楚,CLI / 生成 / 批改可以分开测,后面补 GUI 没有改核心逻辑。

教训:交换律去重要用树结构,不能只把字符串排序;1+2+33+2+1 是作业里专门点名的反例。

posted @ 2026-09-21 08:41  程序员147258369  阅读(13)  评论(0)    收藏  举报