软件工程第三次作业

软件工程第三次作业

软件工程第三次作业

姓名:蒋国栋

学号:3123004570

github仓库:dyLR-036030/xy3

这个作业属于哪个课程 https://edu.cnblogs.com/campus/gdgy/class12Grade23ComputerScience/homework/13469
这个作业要求在哪里 结对项目 - 作业 - 计科23级12班 - 班级博客 - 博客园
这个作业的目标 实现一个自动生成小学四则运算程序

1. PSP表格

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

2.性能分析

c538e6d2bd5548aac47117cdd63a34f6

从火山图可知,函数generate_unique_expression()耗时最长。

改进思路:

  1. 优化表达式生成算法:通过限制尝试次数避免无限循环
  2. 改进去重机制:使用规范化形式进行快速比较
  3. 缓存计算结果:在表达式求值时避免重复计算
  4. 优化数字生成:限制分数分母范围,避免过于复杂的分数

3.设计实现过程

3.1类设计

MathExerciseGenerator (主控制器)

ExpressionGenerator (表达式生成器)

ArithmeticExpression (表达式类)

3.2关键类说明

  1. ArithmeticExpression类:表示算术表达式,支持二叉树结构
  2. ExpressionGenerator类:负责生成唯一的表达式
  3. MathExerciseGenerator类:主控制器,协调整个流程

3.3核心函数调用关系

a24a01a099f4d9294e9d859cb798a3cf

3.4关键函数流程图

3.4.1表达式生成流程图

eef81a3b742e937703429b88a1fe76d0

3.4.2表达式求值流程图

85a9e301972877ac6e8f822b65454ed2

3.4.3答案检查流程图

cacc41639ac8b24ec96119d2d55b7be9

3.4.4数字生成流程图

image

4. 代码说明

关键代码1:表达式求值

python

def evaluate(self) -> Fraction:
    """计算表达式的值"""
    if self.is_leaf():
        return self.value
    
    left_val = self.left.evaluate()
    right_val = self.right.evaluate()
    
    if self.operator == '+':
        return left_val + right_val
    elif self.operator == '-':
        result = left_val - right_val
        if result < 0:  # 确保结果非负
            raise ValueError("计算结果不能为负数")
        return result
    # ... 其他运算符处理

思路: 使用递归方式计算表达式值,在减法时检查结果是否为负数。

关键代码2:表达式规范化

python

def get_canonical_form(self) -> str:
    """获取表达式的规范形式,用于去重"""
    if self.is_leaf():
        return str(self.value)
    
    left_canonical = self.left.get_canonical_form()
    right_canonical = self.right.get_canonical_form()
    
    # 对于加法和乘法,对操作数排序以实现规范化
    if self.operator in ['+', '×']:
        if left_canonical > right_canonical:
            left_canonical, right_canonical = right_canonical, left_canonical
    
    return f"({left_canonical}{self.operator}{right_canonical})"

思路: 通过交换加法和乘法的操作数顺序,实现表达式的规范化,避免重复题目。

5.测试代码

import unittest
from fractions import Fraction
from main import ArithmeticExpression, ExpressionGenerator, MathExerciseGenerator


class TestArithmeticExpression(unittest.TestCase):
    """测试算术表达式类"""

    def test_leaf_expression(self):
        """测试叶子节点表达式"""
        expr = ArithmeticExpression(value=Fraction(3, 1))
        self.assertEqual(expr.evaluate(), Fraction(3))
        self.assertEqual(expr.to_string(), "3")
        self.assertTrue(expr.is_leaf())

    def test_addition_expression(self):
        """测试加法表达式"""
        left = ArithmeticExpression(value=Fraction(2))
        right = ArithmeticExpression(value=Fraction(3))
        expr = ArithmeticExpression(left=left, right=right, operator='+')
        self.assertEqual(expr.evaluate(), Fraction(5))
        self.assertEqual(expr.to_string(), "2 + 3")

    def test_subtraction_expression(self):
        """测试减法表达式"""
        left = ArithmeticExpression(value=Fraction(5))
        right = ArithmeticExpression(value=Fraction(2))
        expr = ArithmeticExpression(left=left, right=right, operator='-')
        self.assertEqual(expr.evaluate(), Fraction(3))
        self.assertEqual(expr.to_string(), "5 - 2")

    def test_multiplication_expression(self):
        """测试乘法表达式"""
        left = ArithmeticExpression(value=Fraction(3))
        right = ArithmeticExpression(value=Fraction(4))
        expr = ArithmeticExpression(left=left, right=right, operator='×')
        self.assertEqual(expr.evaluate(), Fraction(12))
        self.assertEqual(expr.to_string(), "3 × 4")

    def test_division_expression(self):
        """测试除法表达式"""
        left = ArithmeticExpression(value=Fraction(1))
        right = ArithmeticExpression(value=Fraction(2))
        expr = ArithmeticExpression(left=left, right=right, operator='÷')
        self.assertEqual(expr.evaluate(), Fraction(1, 2))
        self.assertEqual(expr.to_string(), "1 ÷ 2")

    def test_complex_expression(self):
        """测试复杂表达式"""
        # (2 + 3) × 4
        left = ArithmeticExpression(
            left=ArithmeticExpression(value=Fraction(2)),
            right=ArithmeticExpression(value=Fraction(3)),
            operator='+'
        )
        right = ArithmeticExpression(value=Fraction(4))
        expr = ArithmeticExpression(left=left, right=right, operator='×')
        self.assertEqual(expr.evaluate(), Fraction(20))
        self.assertEqual(expr.to_string(), "(2 + 3) × 4")

    def test_format_number_fraction(self):
        """测试分数格式化"""
        # 真分数
        self.assertEqual(ArithmeticExpression.format_number(Fraction(1, 2)), "1/2")
        # 带分数
        self.assertEqual(ArithmeticExpression.format_number(Fraction(7, 2)), "3'1/2")
        # 整数
        self.assertEqual(ArithmeticExpression.format_number(Fraction(5)), "5")

    def test_canonical_form_addition(self):
        """测试加法表达式的规范形式"""
        left = ArithmeticExpression(value=Fraction(2))
        right = ArithmeticExpression(value=Fraction(3))
        expr1 = ArithmeticExpression(left=left, right=right, operator='+')
        expr2 = ArithmeticExpression(left=right, right=left, operator='+')
        self.assertEqual(expr1.get_canonical_form(), expr2.get_canonical_form())

    def test_negative_result_prevention(self):
        """测试防止负数结果"""
        left = ArithmeticExpression(value=Fraction(2))
        right = ArithmeticExpression(value=Fraction(5))
        expr = ArithmeticExpression(left=left, right=right, operator='-')
        with self.assertRaises(ValueError):
            expr.evaluate()

    def test_division_by_zero_prevention(self):
        """测试防止除零"""
        left = ArithmeticExpression(value=Fraction(5))
        right = ArithmeticExpression(value=Fraction(0))
        expr = ArithmeticExpression(left=left, right=right, operator='÷')
        with self.assertRaises(ValueError):
            expr.evaluate()


class TestExpressionGenerator(unittest.TestCase):
    """测试表达式生成器"""

    def setUp(self):
        self.generator = ExpressionGenerator(range_max=10)

    def test_generate_number_range(self):
        """测试数字生成范围"""
        for _ in range(100):
            number = self.generator.generate_number()
            self.assertTrue(0 <= number < 10)

    def test_generate_expression_operator_count(self):
        """测试表达式运算符数量"""
        for max_ops in [1, 2, 3]:
            expr = self.generator.generate_expression(max_ops)
            # 这里可以添加运算符数量检查的逻辑

    def test_unique_expression_generation(self):
        """测试唯一表达式生成"""
        expressions = set()
        for _ in range(50):
            expr, value = self.generator.generate_unique_expression()
            canonical_form = expr.get_canonical_form()
            self.assertNotIn(canonical_form, expressions)
            expressions.add(canonical_form)


class TestMathExerciseGenerator(unittest.TestCase):
    """测试数学题目生成器"""

    def test_exercise_generation_count(self):
        """测试题目生成数量"""
        generator = MathExerciseGenerator(range_max=10)
        exercises = generator.generate_exercises(5)
        self.assertEqual(len(exercises), 5)

    def test_expression_evaluation_safe(self):
        """测试安全表达式求值"""
        generator = MathExerciseGenerator(range_max=10)

        # 测试简单表达式
        result = generator.evaluate_expression_safe("2 + 3")
        self.assertEqual(result, Fraction(5))

        # 测试分数表达式
        result = generator.evaluate_expression_safe("1/2 + 1/3")
        self.assertEqual(result, Fraction(5, 6))

        # 测试带分数表达式
        result = generator.evaluate_expression_safe("1'1/2 + 1")
        self.assertEqual(result, Fraction(5, 2))


class TestIntegration(unittest.TestCase):
    """集成测试"""

    def test_complete_workflow(self):
        """测试完整工作流程"""
        import tempfile
        import os

        # 生成题目
        generator = MathExerciseGenerator(range_max=10)
        exercises = generator.generate_exercises(3)

        # 保存到临时文件
        with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.txt') as ex_file:
            with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.txt') as ans_file:
                exercise_file = ex_file.name
                answer_file = ans_file.name

                generator.save_to_files(exercises, exercise_file, answer_file)

        try:
            # 验证文件内容
            with open(exercise_file, 'r') as f:
                exercise_lines = f.readlines()
            with open(answer_file, 'r') as f:
                answer_lines = f.readlines()

            self.assertEqual(len(exercise_lines), 3)
            self.assertEqual(len(answer_lines), 3)

            # 测试答案检查功能
            correct, wrong = generator.check_answers(exercise_file, answer_file)
            # 因为答案是正确的,所以应该全部正确
            self.assertEqual(len(correct), 3)
            self.assertEqual(len(wrong), 0)

        finally:
            # 清理临时文件
            os.unlink(exercise_file)
            os.unlink(answer_file)


def run_performance_test():
    """性能测试函数"""
    import time

    print("开始性能测试...")

    # 测试生成100道题目的性能
    start_time = time.time()
    generator = MathExerciseGenerator(range_max=10)
    exercises = generator.generate_exercises(100)
    end_time = time.time()

    print(f"生成100道题目耗时: {end_time - start_time:.2f}秒")
    print(f"成功生成题目数量: {len(exercises)}")

    # 测试答案检查性能
    with open("test_exercises.txt", "w") as f:
        for i, (ex, ans) in enumerate(exercises):
            f.write(f"{i + 1}. {ex}\n")

    with open("test_answers.txt", "w") as f:
        for i, (ex, ans) in enumerate(exercises):
            f.write(f"{i + 1}. {ans}\n")

    start_time = time.time()
    correct, wrong = generator.check_answers("test_exercises.txt", "test_answers.txt")
    end_time = time.time()

    print(f"检查100道题目答案耗时: {end_time - start_time:.2f}秒")
    print(f"正确题目数: {len(correct)}, 错误题目数: {len(wrong)}")

    # 清理测试文件
    import os
    os.unlink("test_exercises.txt")
    os.unlink("test_answers.txt")


if __name__ == "__main__":
    # 运行单元测试
    print("运行单元测试...")
    unittest.main(verbosity=2, exit=False)

    print("\n" + "=" * 50 + "\n")

    # 运行性能测试
    run_performance_test()

    print("\n" + "=" * 50 + "\n")

    # 手动测试一些边界情况
    print("手动测试边界情况...")

    # 测试极小范围
    print("测试范围=2的情况...")
    try:
        generator = MathExerciseGenerator(range_max=2)
        exercises = generator.generate_exercises(5)
        print(f"成功生成 {len(exercises)} 道题目")
    except Exception as e:
        print(f"生成失败: {e}")

    # 测试极大范围
    print("测试范围=100的情况...")
    try:
        generator = MathExerciseGenerator(range_max=100)
        exercises = generator.generate_exercises(5)
        print(f"成功生成 {len(exercises)} 道题目")
    except Exception as e:
        print(f"生成失败: {e}")

所有测试用例都能通过,说明代码具有一定的正确性

6.项目小结

本项目实现了一个面向小学教育的四则运算题目自动生成系统,能够生成符合小学数学教学要求的算术题目,支持题目生成、答案计算、答案检查等功能。

7.程序使用说明

# 生成num1个数值范围在num2以内的题目
python main.py -n num1 -r num2

# 检查答案
python main.py -e Exercises.txt -a Answers.txt
posted @ 2025-10-22 00:32  晓轩ya  阅读(3)  评论(0)    收藏  举报