结对项目

算术题目生成器

这个作业属于哪个课程 <计科22级34班>
这个作业要求在哪里 <作业要求>
这个作业的目标 培养学生的综合运用编程技能、问题解决能力和团队协作能力
团队成员 学号
吴秋雪 3222004892
何晓漫 3222004765

项目 GitHub 链接:https://github.com/Tracywu1/FourOperationsProblemGenerator

🧩一、PSP表格

PSP各个阶段 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 50 50
· Estimate · 明确需求和其他因素,估计以下的各个任务需要多少时间 50 50
Development 开发 390 435
· Analysis · 需求分析 (包括学习新技术、新工具的时间) 60 50
· Design Spec · 生成设计文档(整体框架的设计,各模块的接口,用时序图,快速原型等方法) 60 60
· Design Review · 设计复审 10 5
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 20 10
· Design · 具体设计 30 20
· Coding · 具体编码 120 200
· Code Review · 代码复审 30 30
· Test · 测试(自我测试,修改代码,提交修改) 60 60
Reporting 报告 160 180
· Test Report · 测试报告(发现了多少bug,修复了多少) 30 50
· Size Measurement · 计算工作量(多少行代码,多少次签入,多少测试用例,其他工作量) 10 10
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划(包括写文档、博客的时间) 120 120
· 合计 570 665

🧩二、效能分析

下图是用 PyCharm 内置的效能分析工具生成的:

1.效能分析:

  1. 主要瓶颈
    • main 函数耗时最长,占用了总运行时间的99.6%(18340 ms)。这是程序的核心入口点,大部分时间都花在这里。
  2. 重要子函数
    • mainloop 函数占用了总时间的97.9%(18021 ms),是 main 函数中最耗时的部分。这可能是因为它包含了主要的事件循环。
    • <method 'mainloop' of '_tkinter.tkapp' objects> 占用了70.9%的时间,这是 Tkinter GUI 的主循环,说明大部分时间都花在了等待和处理用户界面事件上。
  3. GUI 相关操作
    • __init__ 方法(可能是 GUI 初始化)占用了较少的时间,说明 GUI 的初始化不是性能瓶颈。

2.改进思路:

  1. 优化主事件循环:由于大部分时间都花在了 mainloop 和 Tkinter 的事件处理上,可以考虑优化 GUI 更新频率或使用更轻量级的 GUI 框架。
  2. 异步处理:考虑将一些耗时的操作(如文件 I/O 或复杂计算)放在后台线程中执行,以提高 GUI 的响应性。
  3. 缓存优化:如果有重复的计算或数据获取,可以考虑使用缓存来减少重复操作。
  4. 代码优化:虽然生成表达式的函数不是主要瓶颈,但仍可以考虑优化算法,特别是如果需要生成大量表达式时。
  5. 性能分析:对 mainloop 内部进行更细致的性能分析,找出可能的优化点。

总的来说,这个项目的主要性能瓶颈在于 GUI 的事件循环,而不是算术表达式的生成或计算部分。优化应该主要集中在提高 GUI 的响应性和效率上。但是由于总花费时间只有 18340 ms,并且我们在使用过程中并未觉得 GUI 的响应慢以及效率低,故我们认为该项目没有改进的必要性,项目效能已经达到较高标准。

🧩三、 设计实现过程

1.类结构设计:

  1. Expression:表示一个基本的表达式
  2. Operation:继承自Expression,表示一个运算操作
  3. ExpressionGenerator:负责生成单个表达式
  4. ArithmeticProblemGenerator:负责生成整套算术题目
  5. ArithmeticGUI:提供图形用户界面

2.类之间的关系如下:

  • OperationExpression 的子类
  • ExpressionGenerator 使用 ExpressionOperation 来生成表达式
  • ArithmeticProblemGenerator 使用 ExpressionGenerator 来生成多个题目
  • ArithmeticGUI 使用 ArithmeticProblemGenerator 来生成题目并提供用户界面

3.关键函数流程图:

graph TD A[开始] --> B[初始化计数器和集合] B --> C[生成表达式] C --> D{是否为有效操作?} D -->|否| C D -->|是| E[生成问题签名] E --> F{签名是否重复?} F -->|是| C F -->|否| G[规范化表达式] G --> H{规范化表达式是否重复?} H -->|是| C H -->|否| I[添加到问题集] I --> J{是否达到所需数量?} J -->|否| C J -->|是| K[结束]

这个流程图展示了 ArithmeticProblemGenerator 类中 generate_problems 方法的主要逻辑。

🧩四、代码说明

1.Expression 类

class Expression:
    def __init__(self, value):
        self.value = value
        self.is_operation = False
        self.operator_count = 0

    def evaluate(self):
        return self.value

    def to_string(self):
        return str(self)

Expression类是最基本的表达式单元,可以是一个数值或者一个复杂的表达式。

2.Operation 类

class Operation(Expression):
    def __init__(self, left, right, operator):
        super().__init__(None)
        self.left = left
        self.right = right
        self.operator = operator
        self.is_operation = True
        self.operator_count = left.operator_count + right.operator_count + 1

        if not self.is_valid_operation():
            raise ValueError("Invalid operation")

    def is_valid_operation(self):
        # 检查运算的有效性
        # ...(省略具体实现)

    def evaluate(self):
        # 根据运算符计算结果
        # ...(省略具体实现)

Operation类继承自Expression,表示一个具体的运算操作。它包含左右两个操作数和一个运算符。

3.ExpressionGenerator 类

class ExpressionGenerator:
    def __init__(self, max_value):
        self.max_value = max_value

    def generate_number(self):
        # 生成随机数或分数
        # ...(省略具体实现)

    def generate_expression(self, max_operators=3):
        # 递归生成表达式
        # ...(省略具体实现)

ExpressionGenerator类负责生成随机的表达式,可以是单个数字、分数或复杂的四则运算表达式。

4.ArithmeticProblemGenerator 类

class ArithmeticProblemGenerator:
    def __init__(self, num_problems, max_value):
        self.num_problems = num_problems
        self.max_value = max_value
        self.expression_generator = ExpressionGenerator(max_value)
        self.problems = set()

    def generate_problems(self):
        # 生成指定数量的不重复题目
        # ...(省略具体实现)

    def save_problems(self):
        # 将生成的题目保存到文件
        # ...(省略具体实现)

    def save_answers(self):
        # 计算并保存答案到文件
        # ...(省略具体实现)

ArithmeticProblemGenerator类是整个程序的核心,负责生成完整的算术题目集,并将题目和答案保存到文件中。

🧩五、测试运行

为了确保程序的正确性,我们进行了以下测试:

1.测试简单表达式生成:

expr = Expression(5)
assert str(expr) == "5"
assert expr.evaluate() == 5

2.测试有效运算:

op = Operation(Expression(3), Expression(2), '+')
assert str(op) == "(3 + 2)"
assert op.evaluate() == 5

3.测试无效运算(除以零):

with pytest.raises(ValueError):
    Operation(Expression(3), Expression(0), '÷')

4.测试复杂表达式:

left = Operation(Expression(3), Expression(2), '+')
right = Expression(4)
op = Operation(left, right, '×')
assert str(op) == "((3 + 2) × 4)"
assert op.evaluate() == 20

5.测试随机数生成范围:

generator = ExpressionGenerator(10)
for _ in range(100):
    num = generator.generate_number()
    assert isinstance(num, (int, Fraction))
    if isinstance(num, int):
        assert 0 <= num < 10
    else:
        assert 0 < num.numerator <= 10
        assert 0 < num.denominator <= 10

6.测试表达式生成复杂度:

generator = ExpressionGenerator(10)
for _ in range(100):
    expr = generator.generate_expression()
    assert expr.operator_count <= 3

7.测试问题生成数量:

generator = ArithmeticProblemGenerator(10, 10)
generator.generate_problems()
assert len(generator.problems) == 10

8.测试问题唯一性:

generator = ArithmeticProblemGenerator(100, 10)
generator.generate_problems()
assert len(generator.problems) == len(set(generator.problems))

9.测试表达式计算:

generator = ArithmeticProblemGenerator(1, 10)
assert generator.evaluate_expression("2 + 3") == 5
assert generator.evaluate_expression("6 - 4") == 2
assert generator.evaluate_expression("3 × 4") == 12
assert generator.evaluate_expression("8 ÷ 2") == 4

10.测试结果格式化:

generator = ArithmeticProblemGenerator(1, 10)
assert generator.format_result(5) == "5"
assert generator.format_result(Fraction(1, 2)) == "1/2"
assert generator.format_result(Fraction(5, 2)) == "2'1/2"
assert generator.format_result(3.5) == "3.5"

通过这些测试,我们可以确保程序的各个组件都能正确工作,包括表达式生成、运算执行、问题生成、结果计算和格式化等关键功能。这些测试覆盖了各种可能的情况,包括边界条件和特殊情况,从而增强了我们对程序正确性的信心。

🧩六、 项目小结

1.成功之处

  1. 模块化设计:我们成功地将程序分解为多个独立的类,每个类负责特定的功能,这使得代码易于理解和维护。
  2. 递归算法:使用递归方法生成表达式是一个巧妙的设计,它使得生成复杂表达式变得简单。
  3. 全面的测试:我们编写了详细的单元测试,覆盖了主要功能和边界情况,这大大提高了程序的可靠性。

2.需要改进的地方

  1. 性能优化:在生成大量题目时,程序可能会变慢。我们可以考虑使用缓存或其他优化技术来提高性能。
  2. 用户界面:虽然我们实现了基本的GUI,但它可以进一步改进,使其更加用户友好。
  3. 题目难度控制:目前,题目的难度主要由最大值和操作符数量控制。我们可以考虑添加更细致的难度控制机制。

3.结对感受

结对编程是一次很好的学习经历。通过实时讨论和代码审查,我们能够及时发现和纠正错误,同时也学习到了彼此的编程技巧和思维方式。

4.闪光点和建议

  • 漫:雪在设计类结构时的思路很清晰,这帮助我们快速搭建了程序的框架。建议在今后的项目中,可以更多地运用设计模式来优化代码结构。
  • 雪:漫在编写单元测试时很细心,考虑了许多边界情况,这大大提高了程序的健壮性。建议在编写主要功能的同时就开始编写测试,这样可以更早地发现和解决问题。

总的来说,这个项目让我们深入理解了面向对象编程和测试驱动开发的重要性,也让我们体会到了结对编程的价值。我们期待在未来的项目中能够继续改进和应用这些经验。

posted @ 2024-09-26 21:54  Tracywu1  阅读(19)  评论(0编辑  收藏  举报