结对项目

姓名 学号 github
黄旭彪 3123003446 https://github.com/Ryon-h/Elementary-Arithmetic-Exercise-Generater
郑岱扬 3123004812

psp2.1

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 120 140
· Estimate 估计这个任务需要多少时间 120 140
Development 开发 300 455
· Analysis 需求分析 (包括学习新技术) 60 60
· Design Spec 生成设计文档 20 45
· Design Review 设计复审 30 30
· Coding Standard 代码规范 (为目前的开发制定合适的规范) 10 10
· Design 具体设计 30 30
· Coding 具体编码 120 90
· Code Review 代码复审 30 40
· Test 测试(自我测试,修改代码,提交修改) 60 150
Reporting 报告 80 285
· Test Report 测试报告 30 120
· Size Measurement 计算工作量 20 40
· Postmortem & Process Improvement Plan 事后总结, 并提出过程改进计划 30 125
Total 合计 500(8小时) 880(13小时)

性能优化过程记录

优化时间分配:

  1. 代码分析:30分钟

  2. 性能瓶颈识别:45分钟

  3. 优化实施:1.5小时

  4. 测试验证:1小时
    总计:3.75小时
    优化思路:

  5. 使用性能分析工具(如cProfile)识别热点函数

  6. 优化数据结构和算法复杂度

  7. 实现并行处理

  8. 减少I/O操作频率

  9. 使用缓存机制

优化建议

  1. 使用批量处理替代频繁的文件I/O
  2. 引入多线程处理大量数据
  3. 优化数据结构,减少内存占用
  4. 使用缓存机制减少重复计算
  5. 采用更高效的算法实现

性能分析图表

我创建了性能分析函数profiling,程序会自动执行三组不同规模的测试用例(100题、1000题和10000题),并为每组测试显示性能分析结果。分析结果会显示最耗时的10个函数,包括它们的调用次数、累计时间等信息,这些信息可以帮助我们找出程序中的性能瓶颈。

整体架构设计

项目采用面向对象的设计方法,主要包含以下核心模块:

  1. 主程序模块 ( main.py )

    • 负责程序入口
    • 处理命令行参数
    • 协调其他模块工作
  2. 算术题目生成器 ( arithmetic_generator.py )

    • 核心类: ArithmeticGenerator
    • 负责生成算术表达式
    • 计算表达式结果
  3. 答案检查器 ( answer_checker.py )

    • 核心类: AnswerChecker
    • 负责验证答案正确性
    • 生成检查报告

数据流向

  1. 命令行参数 → Main程序
  2. Main程序 → ArithmeticGenerator(生成题目)
  3. ArithmeticGenerator → 文件系统(保存题目和答案)
  4. Main程序 → AnswerChecker(检查答案)
  5. AnswerChecker → 文件系统(读取答案)
  6. AnswerChecker → 控制台(输出结果)

扩展性考虑

  1. 题目生成器支持扩展新的运算符
  2. 答案检查器支持不同格式的答案文件
  3. 可以轻松添加新的输出格式
  4. 支持自定义题目难度和规则

错误处理

  1. 文件操作异常处理
  2. 输入参数验证
  3. 计算过程异常处理
  4. 格式解析错误处理

函数关系图

主程序流程图

题目生成器流程图

答案检查器流程图

代码展示与说明

main函数

`import argparse
from arithmetic_generator import ArithmeticGenerator
# 导入答案检查模块
from answer_checker import AnswerChecker

def parse_arguments():
    """解析命令行参数"""
    parser = argparse.ArgumentParser(description='四则运算题目生成程序')
    parser.add_argument('-n', type=int, default=10, help='要生成的题目数量')
    parser.add_argument('-r', type=int, default=10, help='数值范围')
    parser.add_argument('-e', type=str, help='题目文件')
    parser.add_argument('-a', type=str, help='答案文件')
    return parser.parse_args()

def main():
    args = parse_arguments()
    
    # 如果提供了题目文件和答案文件,则进行答案校验
    if args.e and args.a:
        checker = AnswerChecker()
        checker.check_answers(args.e, args.a)
    # 否则生成新的题目
    else:
        generator = ArithmeticGenerator(args.r)
        exercises = generator.generate_exercises(args.n)
        
        # 将题目和答案写入文件
        with open('Exercises.txt', 'w', encoding='utf-8') as f:
            for i, exercise in enumerate(exercises, 1):
                f.write(f'{i}. {exercise["question"]}\n')
        
        with open('Answers.txt', 'w', encoding='utf-8') as f:
            for i, exercise in enumerate(exercises, 1):
                f.write(f'{i}. {exercise["answer"]}\n')
        
        print(f'已生成{args.n}道题目,数值范围为{args.r}')
        print('题目已保存到 Exercises.txt')
        print('答案已保存到 Answers.txt')

if __name__ == '__main__':
    main()`

arithmeticgenerato类

`import argparse
import random
import fractions
from typing import List, Set, Tuple, Union

class ArithmeticGenerator:
    def __init__(self, range_limit: int):
        self.range_limit = range_limit
        self.generated_expressions: Set[str] = set()
    
    def generate_number(self) -> Union[int, str]:
        """生成一个随机数(整数或真分数)"""
        # 随机决定生成整数还是分数
        if random.random() < 0.7:  # 70%概率生成整数
            return random.randint(0, self.range_limit - 1)
        else:
            # 生成真分数
            numerator = random.randint(1, self.range_limit - 1)
            denominator = random.randint(numerator + 1, self.range_limit)
            # 如果分子大于分母,转换为带分数格式
            if numerator >= denominator:
                whole = numerator // denominator
                numerator = numerator % denominator
                return f"{whole}'{numerator}/{denominator}" if numerator != 0 else str(whole)
            return f"{numerator}/{denominator}"
    
    def evaluate_expression(self, expr: str) -> Union[int, fractions.Fraction]:
        """计算表达式的值"""
        def parse_number(num_str: str) -> Union[int, fractions.Fraction]:
            if '/' in num_str:
                if "'" in num_str:
                    whole, frac = num_str.split("'")
                    num, den = map(int, frac.split("/"))
                    return fractions.Fraction(int(whole) * den + num, den)
                else:
                    num, den = map(int, num_str.split("/"))
                    return fractions.Fraction(num, den)
            return int(num_str)

        # 将表达式转换为后缀表达式
        def infix_to_postfix(expr: str) -> List[str]:
            precedence = {'+': 1, '-': 1, '×': 2, '÷': 2}
            stack = []
            output = []
            tokens = expr.replace('(', ' ( ').replace(')', ' ) ').split()

            for token in tokens:
                if token in '+-×÷':
                    while (stack and stack[-1] != '(' and 
                           precedence[stack[-1]] >= precedence[token]):
                        output.append(stack.pop())
                    stack.append(token)
                elif token == '(':
                    stack.append(token)
                elif token == ')':
                    while stack and stack[-1] != '(':
                        output.append(stack.pop())
                    if stack:
                        stack.pop()
                else:
                    output.append(token)

            while stack:
                output.append(stack.pop())

            return output

        # 计算后缀表达式
        def evaluate_postfix(tokens: List[str]) -> Union[int, fractions.Fraction]:
            stack = []
            for token in tokens:
                if token in '+-×÷':
                    b = stack.pop()
                    a = stack.pop()
                    if token == '+':
                        stack.append(a + b)
                    elif token == '-':
                        if b > a:  # 检查是否会产生负数
                            raise ValueError("产生负数")
                        stack.append(a - b)
                    elif token == '×':
                        stack.append(a * b)
                    elif token == '÷':
                        if b == 0:
                            raise ValueError("除数为零")
                        result = a / b
                        if result >= 1:
                            raise ValueError("除法结果不是真分数")
                        stack.append(result)
                else:
                    stack.append(parse_number(token))
            return stack[0]

        try:
            return evaluate_postfix(infix_to_postfix(expr))
        except Exception as e:
            raise ValueError(f"表达式计算错误: {str(e)}")

    def is_valid_expression(self, expr: str) -> bool:
        """检查表达式是否有效(不产生负数且除法结果为真分数)"""
        try:
            self.evaluate_expression(expr)
            return True
        except ValueError:
            return False

    def generate_expression(self) -> str:
        """生成一个随机算术表达式"""
        operators = ['+', '-', '×', '÷']
        max_operators = random.randint(1, 3)  # 随机决定使用1-3个运算符
        
        # 生成第一个数
        expr = str(self.generate_number())
        
        # 添加运算符和数字
        for _ in range(max_operators):
            op = random.choice(operators)
            num = str(self.generate_number())
            
            # 随机决定是否添加括号
            if random.random() < 0.3 and op in '×÷':  # 30%概率添加括号
                expr = f"({expr})"
            
            expr = f"{expr} {op} {num}"
            
            # 验证当前表达式是否有效
            if not self.is_valid_expression(expr):
                # 如果无效,回退到上一个有效状态
                return self.generate_expression()
        
        return f"{expr} ="
    
    def generate_exercises(self, count: int) -> List[dict]:
        """生成指定数量的习题"""
        exercises = []
        while len(exercises) < count:
            expr = self.generate_expression()
            if expr not in self.generated_expressions:
                try:
                    # 去掉表达式末尾的等号
                    question = expr
                    answer = str(self.evaluate_expression(expr.rstrip(' =')))
                    exercises.append({"question": question, "answer": answer})
                    self.generated_expressions.add(expr)
                except ValueError:
                    continue
        return exercises

def main():
    parser = argparse.ArgumentParser(description='四则运算题目生成器')
    group = parser.add_mutually_exclusive_group(required=True)
    group.add_argument('-n', type=int, help='生成题目的数量')
    group.add_argument('-e', type=str, help='练习题文件路径')
    parser.add_argument('-r', type=int, help='数值范围(必需)')
    parser.add_argument('-a', type=str, help='答案文件路径')
    
    args = parser.parse_args()
    
    if args.n:
        if not args.r:
            parser.error('使用-n参数时必须指定-r参数')
        if args.r < 2:
            parser.error('-r参数必须大于1')
            
        generator = ArithmeticGenerator(args.r)
        exercises = generator.generate_exercises(args.n)
        
        # 保存习题到文件
        with open('Exercises.txt', 'w', encoding='utf-8') as f:
            for i, exercise in enumerate(exercises, 1):
                f.write(f'{exercise}\n')
        
        # TODO: 计算答案并保存到文件
        
    elif args.e and args.a:
        # TODO: 实现答案验证功能
        pass

if __name__ == '__main__':
    main()`

anwer_checker

`class AnswerChecker:
def check_answers(self, exercise_file, answer_file):
"""检查答案文件中的答案是否正确,并将结果输出到Grade.txt文件中

    Args:
        exercise_file (str): 题目文件路径
        answer_file (str): 答案文件路径
    """
    # 读取题目文件
    with open(exercise_file, 'r', encoding='utf-8') as f:
        exercises = f.readlines()
    
    # 读取答案文件
    with open(answer_file, 'r', encoding='utf-8') as f:
        answers = f.readlines()
    
    # 检查答案数量是否匹配
    if len(exercises) != len(answers):
        print('错误:题目数量与答案数量不匹配')
        return
    
    correct_indices = []
    wrong_indices = []
    total_count = len(exercises)
    
    # 逐题检查答案
    for i, (exercise, answer) in enumerate(zip(exercises, answers)):
        # 获取题号
        question_num = i + 1
        
        # 去除题号和空白字符
        exercise = exercise.split('.', 1)[1].strip()
        answer = answer.split('.', 1)[1].strip()
        
        # 计算正确答案
        try:
            correct_answer = str(eval(exercise))
            if correct_answer == answer:
                correct_indices.append(question_num)
            else:
                wrong_indices.append(question_num)
        except:
            print(f'警告:第{question_num}题无法计算')
            wrong_indices.append(question_num)
    
    # 将结果写入Grade.txt文件
    with open('Grade.txt', 'w', encoding='utf-8') as f:
        f.write(f'Correct: {len(correct_indices)} {str(tuple(correct_indices)).replace(" ", "")}\n')
        f.write(f'Wrong: {len(wrong_indices)} {str(tuple(wrong_indices)).replace(" ", "")}')
    
    # 输出统计结果
    print(f'结果已写入Grade.txt文件')`

这是一个四则运算题目生成器的Python程序,主要包含以下功能:

  1. ArithmeticGenerator类:负责生成算术表达式,包括生成随机数(整数或分数)、验证表达式有效性、计算表达式结果等核心功能。其中generate_number方法生成随机数,evaluate_expression方法计算表达式值,is_valid_expression方法验证表达式合法性,generate_expression方法生成完整算术表达式,generate_exercises方法批量生成习题。
  2. main函数:程序的入口点,处理命令行参数并调用相应功能。目前实现了通过-n参数生成指定数量的习题,并将习题保存到文件的功能。程序支持的参数包括:-n(生成题目数量)、-e(练习题文件路径)、-r(数值范围)、-a(答案文件路径)。
  3. 表达式处理功能:程序使用中缀表达式转后缀表达式的方法来计算表达式结果,支持括号、四则运算,并能正确处理分数运算,同时确保不会产生负数和除法结果为真分数。

测试用例

随机生成random每次用例不同,为了实现grade程序,运行run脚本含有三个用例,分别为

f.write('1. 1 + 2\n')
f.write('2. 3 * 4\n')
f.write('3. 10 - 5\n')

四则运算生成器项目总结

作者:黄旭彪 & 郑岱扬
日期:2025-03-24

项目概述

我们合作开发了一个四则运算题目生成器,这是我们第一次尝试结对编程。项目实现了题目生成、答案验证等核心功能,并且支持命令行操作。

技术实现亮点

  1. 模块化设计

    • 清晰的职责划分
    • 良好的代码组织
    • 高内聚低耦合
  2. 可扩展性

    • 支持自定义题目数量
    • 灵活的数值范围设置
    • 预留了扩展接口

成功经验

合理的分工

  • hxb负责核心算法实现
  • zdy负责测试用例编写
  • 共同完成代码审查

遇到的挑战

1. 技术难点

"最初在处理分数运算时遇到了一些困难,但通过结对编程,我们一起找到了更优的解决方案。"

2. 时间管理

"有时候因为工作时间安排不一致,导致协作效率受影响。后来我们固定了每天的结对时间,情况就好多了。"

改进建议

  1. 开发流程优化

    • 引入自动化测试
    • 完善文档管理
    • 建立代码规范
  2. 团队协作提升

    • 更频繁的代码审查
    • 建立知识共享机制
    • 优化任务分配方式

结对感受

感受

"结对编程让我学会了如何更好地表达自己的想法,同时也从搭档那里学到了很多新的编程技巧。这种合作模式确实能够提高代码质量,减少bug的产生。"
"通过结对编程,我不仅提高了编程能力,还学会了如何更好地与他人合作。实时的代码审查和讨论让我对代码质量有了更深的认识。"

未来展望

  1. 功能扩展

    • 支持更多运算类型
    • 添加难度等级
    • 优化用户界面
  2. 技术提升

    • 引入新的设计模式
    • 优化算法效率
    • 加强代码健壮性

总结

结对编程不仅仅是一种开发模式,更是一种促进团队成长的有效方式。通过这次项目,我们都收获了宝贵的经验,也建立了更好的合作关系。期待在未来的项目中能够继续保持这种高效的协作模式。

关键词

结对编程 ####项目总结 ####经验分享 ####团队协作 ####Python开发

posted @ 2025-03-21 23:19  Ryon-h  阅读(34)  评论(0)    收藏  举报