结对项目

课程 软件工程
这个作业要求在哪里 作业要求
这个作业的目标 实现小学四则运算题目自动生成、自动判题功能

张高朗3124004262

Part 1 github链接

https://github.com/3214153741-sudo/four_calc_project

Part 2 PSP表格(时间预估)

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

Part 3 效能分析

  1. 优化前:递归生成表达式,容易大量产生非法题目(减法负数、除零、假分数),需要大量丢弃重试,效率低。
  2. 改进思路:提前对子表达式求值校验,非法节点直接返回 None,提前剪枝,减少无效递归;增加表达式字符串集合去重,避免重复题目。
  3. 性能分析图:用pyinstrument性能剖析工具,执行批量生成 1000 道四则运算题目,采集程序 CPU 耗时数据,生成交互式性能报告,找到消耗最大的函数:gen_expr() 是最耗时的函数

Part 4 代码实现过程

2 个类

  1. ValueNode:数字叶子节点,代表单个数字(整数 / 分数)
    方法:evaluate() 求值;to_string() 转为字符串表达式
  2. BinOpNode:二元运算节点,保存左子树、运算符、右子树
    方法:evaluate() 求值并校验合法性;to_string() 拼接带括号表达式

4 个核心函数

  1. gen_expr(max_depth, num_range)
    递归生成 AST 表达式树,最耗时核心函数。递归创建左右子节点,构造二元运算节点,求值校验,非法直接返回 None 完成剪枝。
  2. generate_problem_set(count, num_range)
    题目集合生成器,循环调用gen_expr,使用集合去重,收集足够多合法不重复题目,返回题目列表、答案列表。
  3. write_files(problems, answers)
    文件输出函数,把题目写入Exercises.txt,标准答案写入Answers.txt
  4. check_answer()
    判题函数:读取标准答案文件、用户作答文件,逐题比对,统计对错,输出Grade.txt

image

Part 5 代码说明

本次节选核心代码为抽象语法树节点定义、递归表达式生成、合法性校验、题目去重模块。文件读写、判题等辅助功能代码较为简单,不在此处展示,完整源码放在博客末尾附录。

  1. ValueNodeBinOpNode 构成抽象语法树。evaluate() 在求值同时校验规则:减法不能得到负数、除法不能除零、除法结果必须为真分数或整数;非法表达式直接返回None,实现递归剪枝。使用Fraction分数类型,彻底避免浮点数精度丢失问题。
  2. gen_expr():递归生成表达式树,控制嵌套括号深度。生成子表达式后立刻校验,非法直接丢弃,减少无效递归,是性能优化的核心。
  3. generate_problem_set():循环调用递归生成函数,使用集合存储表达式字符串,依靠集合元素唯一性完成题目去重,收集指定数量合法题目。
from fractions import Fraction
import random

# 数字叶子节点:AST树的叶子,保存数值
class ValueNode:
    def __init__(self, val):
        self.val = Fraction(val)
    def evaluate(self):
        return self.val
    def to_string(self):
        return str(self.val)

# 二元运算节点:保存左右子树与运算符,负责求值+合法性校验
class BinOpNode:
    def __init__(self, left, op, right):
        self.left = left
        self.op = op
        self.right = right
    def evaluate(self):
        l_val = self.left.evaluate()
        r_val = self.right.evaluate()
        if self.op == '+':
            return l_val + r_val
        elif self.op == '-':
            res = l_val - r_val
            return res if res >= 0 else None
        elif self.op == '*':
            return l_val * r_val
        elif self.op == '÷':
            if r_val == 0:
                return None
            res = l_val / r_val
            return res if res.numerator <= res.denominator else None
    def to_string(self):
        return f"({self.left.to_string()}{self.op}{self.right.to_string()})"

# 核心递归函数:生成表达式AST,递归剪枝
def gen_expr(max_depth, num_range=10):
    if max_depth <= 1:
        return ValueNode(random.randint(1, num_range))
    ops = ['+', '-', '*', '÷']
    op = random.choice(ops)
    left = gen_expr(max_depth - 1, num_range)
    right = gen_expr(max_depth - 1, num_range)
    node = BinOpNode(left, op, right)
    return node if node.evaluate() is not None else None

# 批量生成题目,集合去重
def generate_problem_set(count, num_range=10):
    expr_set = set()
    problems, answers = [], []
    while len(problems) < count:
        ast = gen_expr(3, num_range)
        if ast is None: continue
        expr_str = ast.to_string()
        if expr_str not in expr_set:
            expr_set.add(expr_str)
            problems.append(expr_str)
            answers.append(str(ast.evaluate()))
    return problems, answers

Part 6 测试用例

此处我在打包好了的含exe文件的文件夹终端输命令生成10个测试数

此处是生成的十个测试用例,和对应答案及检测之后的结果

说明

打开生成的Exercises.txt,文件内包含 10 条合法四则表达式:表达式减法无负数结果、除法不会出现除零、除法结果仅保留整数或真分数,所有题目不存在重复。
Answers.txt中存储每道题对应的计算结果。
Grade.txt记录每道题的批改判定结果,本次测试使用标准答案进行自检,全部题目判定正确,程序功能符合预期。

Part 7 完善表格

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

Part 8 项目小结

本次独立完成小学四则运算生成器项目,使用 Python 实现核心功能,在 VSCode 环境下开发,最终打包为可独立运行的 exe 程序,达成了全部预期需求:能够随机生成合法四则运算题目,支持分数运算,保证减法结果非负、除法无除零错误,可输出题目文件并自动判题。

  1. 巩固了 Python 模块化编程、命令行参数解析、文件读写的基础能力;
  2. 深入练习了分数处理、表达式合法性校验、随机表达式去重等算法逻辑;
  3. 掌握了 PyInstaller 打包 Python 脚本为 exe 可执行文件的方法,学会处理打包过程中出现的依赖问题;

我觉得后续可以增加图形化界面,不只是命令行交互,降低使用门槛,增加更多题型配置选项,例如可选择是否启用括号、是否只使用整数运算。或者增加错题统计功能,读取用户作答文件,统计错误题目并输出错题集。
(本次项目是结对项目,但是项目是我自己独立完成,并没有体验到结对的感觉,希望下次有机会尝试)

posted @ 2026-09-19 23:32  张高朗  阅读(0)  评论(0)    收藏  举报