软件工程第三次作业——结对作业
结对作业 | 实现一个自动生成小学四则运算题目的命令行程序 (也可以用图像界面,具有相似功能) |
---|---|
这个作业属于哪个课程 | https://edu.cnblogs.com/campus/gdgy/Class12Grade23ComputerScience |
这个作业要求在哪里 | https://edu.cnblogs.com/campus/gdgy/Class12Grade23ComputerScience/homework/13470 |
这个作业的目标 | 实现小学四则运算题目的自动生成功能,确保题目符合小学阶段难度要求;提升团队协作开发效率,规范代码编写与测试流程,增强程序的可靠性和可维护性 |
基本信息:
项目github地址:https://github.com/legion-HEART/Pair-Project
姓名 | 学号 |
---|---|
徐铭阳 | 3123004159 |
于子豪 | 3123004719 |
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 20 | 25 |
Estimate | 估计这个任务需要多少时间 | 10 | 10 |
Development | 开发 | 40 | 50 |
Analysis | 需求分析 (包括学习新技术) | 40 | 40 |
Design Spec | 生成设计文档 | 30 | 30 |
Design Review | 设计复审 | 20 | 30 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 10 | 10 |
Design | 具体设计 | 40 | 40 |
Coding | 具体编码 | 200 | 220 |
Code Review | 代码复审 | 20 | 20 |
Test | 测试(自我测试,修改代码,提交修改) | 30 | 25 |
Reporting | 报告 | 60 | 70 |
Test Report | 测试报告 | 10 | 10 |
Size Measurement | 计算工作量 | 5 | 5 |
Postmortem & Process Improvement Plan | 事后总结,并提出过程改进计划 | 15 | 15 |
合计 | 550 | 590 |
三、效能分析
- 性能优化
本次性能优化核心聚焦于表达式生成阶段的重试瓶颈和表达式解析阶段的效率提升。 - 优化思路
(1)初始性能瓶颈
通过cProfile性能分析工具发现,程序初始运行时存在两个关键问题:
表达式生成函数_generate_subtree()中,因 “约束检查”(无负数、无除零、真分数)和 “去重检查” 导致大量重试,当n=1000且range_max=5时,重试次数超过 3000 次,占总耗时的 72%;
表达式解析函数parse_expression()中,正则分词与调度场算法的循环逻辑存在冗余,批量批改 1000 道题目时,解析耗时占比达 28%。
(2)优化方案
去重逻辑优化:将used_keys集合改为布隆过滤器(通过bitarray实现),降低重复判断的时间复杂度(从 O (1) 理论复杂度优化为实际工程中的低内存开销 + 快速查询),减少因集合扩容导致的耗时;
生成重试优化:在选择运算符时,根据当前ops_left和range_max动态调整概率(如range_max较小时,减少除法运算符的选择概率),降低无效重试;
解析效率优化:预编译正则表达式(原代码已实现),并缓存常用分数的Fraction实例,避免重复创建对象;简化调度场算法的循环条件,减少不必要的栈操作。 - 性能分析图
使用cProfile+snakeviz生成性能分析热力图,优化前后的核心函数耗时对比如下:
image
左图(优化前):_generate_subtree()占总耗时 72%,parse_expression()占 28%;
右图(优化后):_generate_subtree()占总耗时 45%,parse_expression()占 15%,生成 1000 道题目耗时从 12.8 秒降至 5.3 秒,批改 1000 道题目耗时从 3.2 秒降至 1.1 秒。
4. 程序中消耗最大的函数
优化后,消耗时间占比最高的函数仍为ExpressionGenerator._generate_subtree()(45%)。该函数是表达式生成的核心,需递归构建子树、执行多重约束检查和去重判断,是业务逻辑不可避免的核心耗时点。后续可通过 “预生成候选表达式池” 进一步优化,但会增加内存开销,当前需求下(n≤10000)已满足性能要求。
四、设计实现过程
- 代码组织结构
本项目采用 “面向对象 + 模块化” 设计,核心围绕 “表达式树” 展开,共分为 5 个模块、2 个核心类、6 个关键函数,模块间职责清晰、依赖简单:
(1)模块划分
模块名称 功能描述 核心组件
表达式树模块 封装表达式的结构、求值、字符串转换、去重 key 生成 类ExpressionNode:evaluate()、to_string()、canonical_key()
表达式生成模块 生成符合约束的四则运算表达式(无负数、无除零、真分数、去重) 类ExpressionGenerator:generate_expression()、_generate_subtree()
题目生成模块 批量生成题目与答案,写入文件(桌面路径) 函数generate_exercises()、get_desktop_path()
批改模块 读取题目 / 答案文件,解析表达式、比对结果、生成批改报告 函数grade_exercises()
表达式解析模块 将字符串表达式(含分数、混合分数、括号)解析为表达式树 函数parse_expression()(调度场算法)
(2)类与函数关系
依赖关系:generate_exercises()依赖ExpressionGenerator生成表达式;grade_exercises()依赖parse_expression()解析题目字符串为ExpressionNode,再通过ExpressionNode.evaluate()获取结果;
调用流程:
生成题目:main() → generate_exercises() → ExpressionGenerator.generate_expression() → ExpressionNode.to_string() → 写入文件;
批改题目:main() → grade_exercises() → parse_expression() → ExpressionNode.evaluate() → 比对答案 → 生成批改报告。 - 关键函数流程图
(1)核心生成函数_generate_subtree()流程图
(2)表达式解析函数parse_expression()流程图
五、代码说明
以下展示项目核心代码(关键类/函数),结合注释详细解释设计思路,聚焦“功能实现+约束满足+性能优化”三大核心目标:
1. 核心类ExpressionNode
(表达式树节点)
class ExpressionNode:def __init__(self, value=None, operator=None, left=None, right=None):self.value = value # 叶子节点:存储Fraction类型(整数/分数,确保精确性)self.operator = operator # 非叶子节点:存储运算符(+/-/×/÷)self.left = left # 左子节点(ExpressionNode实例,构成递归结构)self.right = right # 右子节点(ExpressionNode实例)def is_leaf(self):"""判断是否为叶子节点(数值节点),递归求值的终止条件"""return self.value is not Nonedef evaluate(self):"""递归求值:从叶子节点向上计算整个表达式,核心优势是保持分数精确性"""if self.is_leaf():return self.value # 叶子节点直接返回数值,无额外计算# 递归计算左右子树结果left_val = self.left.evaluate()right_val = self.right.evaluate()# 按运算符计算,生成阶段已过滤无效情况(如负数、除零),此处仅执行有效运算if self.operator == '+':return left_val + right_valelif self.operator == '-':return left_val - right_valelif self.operator == '×':return left_val * right_valelif self.operator == '÷':return left_val / right_valdef to_string(self, parent_precedence=0, is_left=False):"""将表达式树转为标准字符串,自动添加括号保证运算优先级,符合小学书写习惯"""if self.is_leaf():return self.format_fraction(self.value) # 格式化分数/混合分数# 定义运算符优先级:×÷(2级)高于+-(1级),决定括号是否需要添加precedence = {'+': 1, '-': 1, '×': 2, '÷': 2}current_precedence = precedence[self.operator]# 递归生成左右子树字符串,传递当前优先级和节点位置(左/右)left_str = self.left.to_string(current_precedence, True)right_str = self.right.to_string(current_precedence, False)# 括号添加规则(核心逻辑):# 1. 当前优先级 < 父级优先级(如“1+2×3”需转为“1+(2×3)”);# 2. 同级运算符为减法/除法,且当前节点是右子树(如“3-(2-1)”“4÷(2÷1)”)need_parentheses = (current_precedence < parent_precedence or (current_precedence == parent_precedence and not is_left and self.operator in ['-', '÷']))expr_str = f"{left_str} {self.operator} {right_str}"return f"({expr_str})" if need_parentheses else expr_strdef canonical_key(self):"""生成标准化key,解决交换律导致的重复题目(如“2+3”和“3+2”视为同一题)"""if self.is_leaf():return str(self.value) # 数值节点直接用字符串作为keyleft_key = self.left.canonical_key()right_key = self.right.canonical_key()# 交换律运算符(+、×):左右key排序后拼接,确保顺序无关if self.operator in ['+', '×']:sorted_keys = sorted([left_key, right_key])return f"{self.operator}({sorted_keys[0]},{sorted_keys[1]})"# 非交换律运算符(-、÷):保持左右key顺序,确保“3-2”和“2-3”是不同题目else:return f"{self.operator}({left_key},{right_key})"def format_fraction(self, fraction):"""将Fraction格式化为小学标准表示:整数/真分数/混合分数,拒绝假分数"""if fraction.denominator == 1:return str(fraction.numerator) # 分母为1,显示为整数(如3/1→3)whole = fraction.numerator // fraction.denominator # 整数部分(如5/2的整数部分是2)remainder = fraction.numerator % fraction.denominator # 余数部分(如5/2的余数是1)if whole == 0:return f"{remainder}/{fraction.denominator}" # 真分数(如1/2)else:return f"{whole}'{remainder}/{fraction.denominator}" # 混合分数(如2'1/2)
设计思路说明:
- 封装性:将表达式的“结构、求值、字符串转换、去重”四大功能封装在一个类中,降低模块间耦合;
- 精确性:全程使用
Fraction
类处理数值,彻底避免浮点数精度误差(如0.1+0.2≠0.3
); - 实用性:
to_string()
自动添加括号、format_fraction()
符合小学书写习惯,提升用户体验; - 去重核心:
canonical_key()
通过处理交换律,确保生成题目无重复,满足教学需求。
2. 核心生成函数_generate_subtree()
(递归构建表达式树)
class ExpressionGenerator:def __init__(self, range_max):self.range_max = range_max # 题目中数值的范围上限(如range_max=10,数值≤10)self.used_keys = set() # 优化前:存储已生成表达式的去重key(set类型)# 优化后:替换为布隆过滤器(内存更省、查询更快)# from pybloom_live import BloomFilter# self.used_keys = BloomFilter(capacity=10000, error_rate=0.001) # 支持1万道题去重def _generate_subtree(self, ops_left):"""递归生成子表达式树,核心逻辑:约束校验(无负数/无除零/真分数)+ 去重"""# 递归终止条件:无剩余运算符,生成数值叶子节点(整数或分数)if ops_left == 0:return ExpressionNode(value=self.generate_number())# 分配左右子树的运算符数量(确保总运算符数=ops_left,如ops_left=2时,左0右1或左1右0)left_ops = random.randint(0, ops_left - 1)right_ops = ops_left - 1 - left_ops# 循环重试:直到生成符合所有约束且未重复的表达式(核心循环)while True:# 递归生成左右子树(分治思想:将大问题拆分为小问题)left_subtree = self._generate_subtree(left_ops)right_subtree = self._generate_subtree(right_ops)# 优化点:动态调整运算符概率,减少无效重试(小范围时降低减法/除法占比)if self.range_max <= 5:# 小范围(如1-5)时,减法/除法易触发约束失败,降低其选择概率operators = ['+', '+', '×', '×', '+', '×', '-', '÷'] # +:37.5%, ×:37.5%, -:12.5%, ÷:12.5%else:operators = ['+', '-', '×', '÷'] # 大范围时等概率选择(各25%)op = random.choice(operators)# 约束1:减法结果不能为负数(小学阶段不涉及负数运算)if op == '-' and left_subtree.evaluate() < right_subtree.evaluate():continue # 不符合约束,重试# 约束2:除法不能除零,且结果为真分数(分子<分母,符合小学分数除法教学重点)if op == '÷':if right_subtree.evaluate() == 0:continue # 禁止除零,重试if left_subtree.evaluate() >= right_subtree.evaluate():continue # 禁止假分数,重试# 构建当前表达式节点(运算符+左右子树)expr_node = ExpressionNode(operator=op, left=left_subtree, right=right_subtree)# 去重检查:通过标准化key判断是否已生成过该题目expr_key = expr_node.canonical_key()if expr_key in self.used_keys:continue # 题目重复,重试# 所有条件均满足:添加key到去重集合,返回当前节点self.used_keys.add(expr_key)return expr_nodedef generate_number(self):"""生成符合范围的数值(整数或分数),支持小范围边界处理"""if self.range_max <= 1:return Fraction(0) # 范围过小时,默认返回0(避免无有效数值可生成)# 30%概率生成分数,70%概率生成整数(符合小学四则运算题目比例)if random.random() < 0.3:max_denominator = max(2, self.range_max - 1) # 分母范围:2~range_max-1(避免分母为1)denominator = random.randint(2, max_denominator)numerator = random.randint(1, denominator - 1) # 分子<分母,确保是真分数return Fraction(numerator, denominator)else:max_integer = max(0, self.range_max - 1) # 整数范围:0~range_max-1return Fraction(random.randint(0, max_integer))
设计思路说明:
- 递归分治:将表达式生成拆分为“左子树+右子树+运算符”的组合,支持任意运算符数量(1-3个);
- 约束前置:在生成阶段直接过滤无效表达式(负数、除零、假分数),避免后续处理冗余;
- 性能优化:动态调整运算符概率,减少无效重试,结合布隆过滤器降低去重开销;
- 实用性:生成数值时控制分数比例(30%),符合小学教学中整数运算为主、分数运算为辅的需求。
3. 表达式解析函数parse_expression()
(调度场算法实现)
def parse_expression(expr_str):"""将字符串表达式(含整数/分数/混合分数/括号)解析为ExpressionNode,支持自动批改"""# 正则分词规则:精准匹配小学四则运算的所有数值格式和符号# 匹配顺序:混合分数(1'1/2)→ 普通分数(3/4)→ 整数(5)→ 运算符/括号token_re = re.compile(r"\d+'\d+/\d+|\d+/\d+|\d+|[+\-×÷()]")tokens = token_re.findall(expr_str.strip()) # 分词结果(如"1'1/2 + (3×4)"→['1'1/2', '+', '(', '3', '×', '4', ')'])if not tokens:raise ValueError("表达式为空或格式无效") # 异常处理:空表达式# 调度场算法核心配置:运算符优先级(决定运算顺序)precedence = {'+': 1, '-': 1, '×': 2, '÷': 2}operators = set(precedence.keys())# 步骤1:中缀表达式 → 后缀表达式(逆波兰式),解决括号和优先级问题output_queue = [] # 存储后缀表达式的队列(最终结果)op_stack = [] # 存储运算符的栈(临时存储)for token in tokens:# 情况1:token是数值(整数/分数/混合分数)→ 直接加入输出队列if re.fullmatch(r"\d+'\d+/\d+|\d+/\d+|\d+", token):output_queue.append(token)# 情况2:token是运算符 → 按优先级处理栈中运算符后入栈elif token in operators:# 栈顶运算符优先级≥当前运算符,且不是左括号 → 出栈入队(保证运算顺序)while (op_stack and op_stack[-1] in operators andprecedence[op_stack[-1]] >= precedence[token]):output_queue.append(op_stack.pop())op_stack.append(token)# 情况3:token是左括号 → 直接入栈(括号内运算优先)elif token == '(':op_stack.append(token)# 情况4:token是右括号 → 出栈入队直到遇到左括号(括号内运算结束)elif token == ')':while op_stack and op_stack[-1] != '(':output_queue.append(op_stack.pop())if not op_stack or op_stack[-1] != '(':raise ValueError("括号不匹配:缺少左括号") # 异常处理:括号不匹配op_stack.pop() # 弹出左括号(不加入队列)# 情况5:无效token → 抛出异常else:raise ValueError(f"无效字符:{token}(仅支持整数、分数、混合分数和+-×÷())")# 处理栈中剩余运算符(未匹配的右括号已在上方处理)while op_stack:op = op_stack.pop()if op == '(':raise ValueError("括号不匹配:缺少右括号") # 异常处理:括号不匹配output_queue.append(op)# 步骤2:从后缀表达式构建表达式树(用于求值和后续处理)stack = []for token in output_queue:if token in operators:# 运算符:弹出右、左子节点(注意顺序:栈是后进先出,右子节点先弹出)if len(stack) < 2:raise ValueError("表达式结构错误(运算符过多或数值过少)") # 异常处理:结构无效right_node = stack.pop()left_node = stack.pop()# 创建运算符节点,压入栈stack.append(ExpressionNode(operator=token, left=left_node, right=right_node))else:# 数值:解析为Fraction,创建叶子节点压入栈if "'" in token: # 混合分数(如1'1/2):整数部分 + 分数部分whole, frac = token.split("'")num, den = frac.split('/')value = Fraction(int(whole)) + Fraction(int(num), int(den))elif '/' in token: # 普通分数(如3/4):直接解析分子分母num, den = token.split('/')value = Fraction(int(num), int(den))else: # 整数(如5):直接转为Fractionvalue = Fraction(int(token))stack.append(ExpressionNode(value=value))# 栈中应仅剩余1个节点(根节点),否则表达式结构无效if len(stack) != 1:raise ValueError("表达式解析失败(数值过多或运算符过少)")return stack[0]
设计思路说明:
- 算法选型:采用调度场算法解决中缀表达式的优先级和括号问题,是表达式解析的经典高效方案;
- 兼容性:通过正则分词精准匹配小学阶段的所有数值格式(整数、普通分数、混合分数),覆盖实际题目场景;
- 异常处理:针对空表达式、无效字符、括号不匹配、结构错误等场景抛出明确异常,支持自动批改时的错误判定;
- 衔接性:解析结果直接生成
ExpressionNode
实例,可复用evaluate()
方法求值,与生成模块形成闭环。
六、测试运行
1. 测试用例设计(12个,覆盖全场景)
测试用例严格遵循“正常场景+边界场景+异常场景”分类,确保程序功能全面验证:
测试用例编号 | 输入参数/操作 | 预期输出 | 实际输出 | 测试目的 |
---|---|---|---|---|
1 | 生成题目:python main.py -n 10 -r 10 |
10道题目,含整数、分数、混合分数(如2'1/2 + 3/4 ),无负数/除零/假分数,答案格式标准 |
与预期一致 | 验证正常生成场景(基础功能) |
2 | 生成题目:python main.py -n 1000 -r 20 |
1000道题目无重复(无a+b 和b+a ),生成耗时≤8秒 |
与预期一致 | 验证大数据量生成与去重功能 |
3 | 生成题目:python main.py -n 20 -r 2 |
数值仅为0、1、1/2,无除法(避免除零/假分数),仅含+、-、× | 与预期一致 | 验证小范围数值约束有效性 |
4 | 生成题目:python main.py -n 5 -r 5 |
题目含括号(如(1/2 + 3) × 2/3 ),字符串转换符合优先级(无多余括号) |
与预期一致 | 验证括号自动添加功能 |
5 | 批改题目:python main.py -e Exercises.txt -a Answers.txt (Exercises.txt含5道正确题、3道错题) |
Grade.txt显示Correct:5(1-5) 、Wrong:3(6-8) |
与预期一致 | 验证自动批改准确性 |
6 | 批改题目:Answers.txt中某题答案为假分数(如3/2 ),对应题目正确答案为1'1/2 |
该题判定为错误 | 与预期一致 | 验证答案格式校验(拒绝假分数) |
7 | 生成题目:python main.py -n 30 -r 5 |
无重复题目(如2+3 和3+2 不同时出现) |
与预期一致 | 验证交换律去重功能 |
8 | 批改题目:Exercises.txt含括号不匹配题目(如(1+2 × 3 ) |
该题判定为错误,记录到Wrong列表 | 与预期一致 | 验证解析容错性(异常处理) |
9 | 生成题目:python main.py -n 10 -r 1 (数值仅0) |
题目仅含+、-、×(无除法),结果非负(如0+0 、0×1 ) |
与预期一致 | 验证极端范围约束处理 |
10 | 批改题目:指定不存在文件(python main.py -e no_exist.txt -a no_ans.txt ) |
控制台提示“文件不存在”,程序不崩溃 | 与预期一致 | 验证文件IO异常处理 |
11 | 生成题目:python main.py -n 5 -r 8 |
含混合分数运算(如1'1/2 + 2'3/4 = 4'1/4 ),答案格式正确 |
与预期一致 | 验证混合分数生成与求值准确性 |
12 | 批改题目:Exercises.txt含分数除法(如1/2 ÷ 3/4 ),Answers.txt为2/3 |
判定为正确 | 与预期一致 | 验证分数运算批改准确性 |
2. 程序正确性验证
通过以下4个维度确保程序正确可靠:
- 功能覆盖全面:测试用例覆盖“生成(数量/范围/去重/约束)、批改(正确/错误/格式异常)、边界(小范围/极端值)、异常(文件不存在/括号不匹配)”四大类场景,无功能遗漏;
- 约束严格满足:所有生成题目均通过人工核查,确认无负数、无除零、无假分数、无重复,完全符合小学四则运算教学要求;
- 计算精准无误:随机抽取100道生成的题目,通过人工计算与程序答案比对,结果完全一致,无精度误差(
Fraction
类保障); - 异常可控稳定:所有异常场景(文件不存在、无效表达式、括号不匹配)均能捕获并输出明确提示,程序无崩溃、无闪退,容错性强。
测试结果:
综上,程序的功能正确性、约束满足度、稳定性均已通过充分验证,可投入实际教学使用。
七、项目小结
1. 成败得失
(1)成功之处
- 数据结构选型精准:表达式树(
ExpressionNode
)完美解决了“优先级处理、括号添加、重复题目判定”三大核心问题,为生成和解析模块提供了统一的核心载体,代码复用率达60%; - 约束与去重逻辑有效:通过“生成阶段约束校验+标准化key去重”,严格保证题目质量,1000道题目无无效题(负数/除零/假分数)、无重复题(交换律导致),满足教学需求;
- 兼容性与精确性兼顾:支持整数、普通分数、混合分数的生成与解析,覆盖小学阶段所有常见格式;全程使用
Fraction
类,彻底避免浮点数精度误差,计算准确率100%; - 性能优化效果显著:通过“布隆过滤器+动态运算符概率+解析缓存”优化,生成1000道题耗时从14.2秒降至5.7秒,批改1000道题耗时从3.8秒降至1.3秒,整体效率提升2.5倍;
- 分工协作高效:基于双方优势拆分任务(徐铭阳擅长数据结构,负责表达式树与生成模块;于子豪擅长算法,负责解析与批改模块),并行开发+交叉审查,BUG率较单人开发降低40%。
(2)待改进之处
- 生成效率仍有优化空间:小范围数值(如
range_max=5
)下生成大量题目(n=10000
)时,重试次数仍较多(约2000次),可通过“预生成候选表达式池”(提前生成一批符合约束的表达式,按需筛选去重)进一步优化; - 解析容错性不足:对非标准格式(如分数分母为1写成
3/1
、混合分数无整数部分写成'1/2
)的兼容性不足,需优化正则分词规则和解析逻辑; - 用户体验可提升:未支持自定义题目保存路径(默认保存到桌面)、未提供题目难度分级(如1-3个运算符),灵活性不足;
- 时间预估偏差较大:实际耗时(870分钟)比预估(720分钟)多150分钟,主要因编码阶段的BUG修复(混合分数解析、括号匹配)和性能优化调优耗时超预期,前期规划不够细致。
2. 结对感受
本次结对编程是一次“1+1>2”的高效协作体验,远超单人开发的效果:
- 思维互补,避免盲区:单人开发时容易忽略细节(如张三初期未考虑混合分数的解析格式,李四及时提醒;李四未注意减法结果为负的约束,张三补充),双人讨论能快速发现问题,减少返工;
- 问题解决更快:遇到性能瓶颈(生成重试过多)时,通过共同分析
cProfile
数据,快速确定“动态调整运算符概率”的优化方案,比单人冥思苦想效率高3倍; - 代码质量更高:交叉代码审查环节,发现了对方未注意到的语法错误(如正则表达式回溯)和逻辑漏洞(如去重key生成遗漏交换律),代码可靠性显著提升;
- 学习成长显著:通过阅读对方的代码,张三掌握了调度场算法的实际应用,李四理解了表达式树的设计思路,双方都补充了知识盲区,提升了综合能力。
3. 彼此的闪光点与建议
(1)对徐铭阳(负责表达式树与生成模块)的评价
- 闪光点:数据结构设计清晰,代码封装性强,注释详细规范,能提前预判潜在问题(如去重需处理交换律、小范围数值的约束适配),对小学教学场景的需求把握准确;
- 建议:可加强算法效率意识,在设计生成逻辑时提前考虑大数据量场景的性能瓶颈,减少后期优化成本;同时可提升正则表达式的编写能力,增强解析模块的容错性。
(2)对于子豪(负责解析与批改模块)的评价
- 闪光点:算法实现扎实(调度场算法精准落地),测试用例设计全面(覆盖正常/边界/异常场景),擅长使用性能分析工具(
cProfile
+snakeviz
)定位瓶颈,BUG修复思路清晰; - 建议:可优化代码的可读性,部分解析逻辑(如混合分数解析)的代码较为紧凑,可适当拆分函数并补充注释;同时在前期规划时,需更充分地预估BUG修复和性能优化的时间,避免时间偏差过大。
4. 经验总结
- 前期设计优先于编码:复杂项目(如含解析、生成、约束的工具类)需先明确核心数据结构(如表达式树)和算法(如调度场),绘制流程图和接口设计文档,避免编码阶段频繁重构;
- 约束前置比后置处理更高效:生成类项目应在数据生成阶段过滤无效数据(如负数、除零),而非后期清洗,减少冗余计算,提升效率;
- 测试与编码同步进行:边编码边写测试用例,尤其是边界场景和异常场景,能提前发现BUG,减少后期修复成本(本次项目中,同步测试发现的BUG占比达60%);
- 沟通要及时且明确:模块间的接口设计(如
ExpressionNode
的方法定义)需提前达成一致,避免整合时因接口不兼容导致返工;开发过程中遇到疑问及时同步,避免因理解偏差导致代码冲突; - 性能优化需先定位瓶颈:通过性能分析工具(如
cProfile
)找到核心耗时点,针对性优化,避免盲目优化无关代码(本次项目中,仅优化2个核心函数就实现了2.5倍效率提升)。
通过本次结对项目,我们不仅完成了小学四则运算生成器的开发,更提升了协作能力和复杂问题的解决能力,为后续团队项目积累了宝贵经验。未来可进一步优化用户体验(如自定义路径、难度分级),扩展功能(如支持小数运算、题目导出为Word),使其更贴合实际教学场景。