结对项目:四则运算自动生成程序

[github地址]
https://github.com/kkrInblU/3222004510.git
https://github.com/115any/3222004512.git

这个作业属于哪个课程 https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/
这个作业要求在哪里 https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/homework/13230
我的搭档 杨殷
这个作业的目标 结对开发程序,实现共同合作

一、PSP表格

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

二、效能分析

记录你在改进程序性能上花费了多少时间,描述你改进的思路,并展示一张性能分析的图。如果可能,展示你程序中消耗最大的函数。(3分)

三、设计实现过程

功能函数

  • format_fraction:格式化分数。
  • is_integer:判断字符串是否为整数。
  • generate_fraction:生成真分数。
  • generate_number:生成自然数或真分数。
  • generate_expression:递归生成四则运算表达式。
  • generate_question:生成一个完整的四则运算题目。
  • evaluate_expression:计算表达式的值。
  • generate_and_save_questions:生成题目和答案并保存到文件。
  • check_answers:检查答案的正确性并统计正确和错误的数量。

实施流程图

flowchart TD Start(开始) --> Init{初始化随机数生成器} Init --> ParseArgs[解析命令行参数] ParseArgs --> CheckArgs{检查参数} CheckArgs -- 是 --> GenSaveQ[generate_and_save_questions] CheckArgs -- 否 --> CheckAns[check_answers] GenSaveQ --> GenExpr[generate_expression] GenExpr --> GenNum[generate_number] GenNum --> GenFrac[generate_fraction] GenExpr --> GenQ[generate_question] GenQ --> EvalExpr[evaluate_expression] EvalExpr --> FormatFrac[format_fraction] CheckAns --> OpenExer[打开练习文件] CheckAns --> OpenAns[打开答案文件] OpenExer --> ReadExer[读取所有练习题目] OpenAns --> ReadAns[读取用户答案] ReadExer --> CalcExpr[计算表达式值] CalcExpr --> EvalExpr ReadAns --> CompareAns[比较标准答案和用户答案] CompareAns -->|答案正确| RecordCorrect[记录正确题目索引] CompareAns -->|答案错误| RecordWrong[记录错误题目索引] RecordCorrect --> SaveGrade[保存成绩报告] RecordWrong --> SaveGrade SaveGrade --> CloseFiles[关闭文件] CloseFiles --> End(结束) classDef startend fill:#f96,stroke:#333,stroke-width:2px; classDef function fill:#9f6,stroke:#333,stroke-width:1px; classDef decision fill:#f96,stroke:#333,stroke-width:1px; class Start,End startend; class ParseArgs,CheckArgs,GenSaveQ,CheckAns,GenExpr,GenNum,GenFrac,GenQ,EvalExpr,FormatFrac,OpenExer,OpenAns,ReadExer,ReadAns,CalcExpr,CompareAns,RecordCorrect,RecordWrong,SaveGrade,CloseFiles function;

函数之间的关系

  • generate_number调用 generate_fraction 来生成分数。
  • generate_expression 调用 generate_number 来生成表达式中的数字或分数。
  • generate_question 调用 generate_expression来生成题目。
  • evaluate_expression调用 format_fraction 来格式化计算结果。
  • generate_and_save_questions 调用 generate_questionevaluate_expression 来生成题目和答案。
  • check_answers 调用 evaluate_expression 来计算标准答案并与用户答案比较。

四、代码说明

生成问题

实施流程

flowchart TD Start(开始生成问题) --> Init{初始化} Init -->|初始化问题和答案列表| GenQA[generate_and_save_questions] GenQA --> GenExpr[generate_question] GenExpr --> Eval[evaluate_expression] Eval --> Check{答案是否有效} Check -- 是 --> Format[format_fraction] Check -- 否 --> GenExpr Format --> Save{保存问题和答案} Save -->|问题数量达到要求| EndGenQA[结束生成问题] Save --> GenExpr EndGenQA --> WriteQ[写入问题到文件] EndGenQA --> WriteA[写入答案到文件] WriteQ --> End(结束) classDef startend fill:#f96,stroke:#333,stroke-width:2px; classDef function fill:#9f6,stroke:#333,stroke-width:1px; classDef decision fill:#f96,stroke:#333,stroke-width:1px; class Start,End startend; class GenQA,Eval,Check,Format,Save function;
    # 生成指定数量的问题和答案,并保存到文件
    def generate_and_save_questions(num_questions, range_num, exercise_filename, answer_filename):
        questions = []  # 存储问题
        answers = []  # 存储答案
        while len(questions) < num_questions:
            question = generate_question(range_num)  # 生成一个问题
            answer = evaluate_expression(question[:-1])  # 计算答案
            if answer != "Error" and all(question not in q for q in questions):
                questions.append(question)  # 如果答案正确且问题不重复,添加到列表
                answers.append(answer)
        with open(exercise_filename, 'w') as ef:  # 打开练习文件
            for item in questions:
                ef.write(item + '\n')  # 写入问题
        with open(answer_filename, 'w') as af:  # 打开答案文件
            for item in answers:
                af.write(item + '\n')  # 写入答案

计算算式的答案

实施流程

flowchart TD Start(开始计算答案) --> Replace[替换错误的单引号] Replace --> Split[分割表达式] Split --> Eval[计算表达式的值] Eval --> Format[format_fraction] Format --> End(结束) classDef startend fill:#f96,stroke:#333,stroke-width:2px; classDef function fill:#9f6,stroke:#333,stroke-width:1px; class Start,End startend; class Replace,Split,Eval,Format function;
    # 计算表达式的值,将带分数转换为加法形式后用 eval 函数求值
    def evaluate_expression(expr):
        expr = expr.replace("'", '+')  # 替换错误的单引号为加号
        try:
            parts = expr.split(' ')  # 分割表达式
            new_expr = ''
            for part in parts:
                if '/' in part:
                    fraction = Fraction(part)  # 如果部分是分数,转换为Fraction对象
                    new_expr += f'{fraction} '  # 添加到新的表达式字符串
                else:
                    new_expr += f'{part} '  # 否则直接添加
            result = eval(new_expr)  # 计算表达式的值
            return format_fraction(Fraction(result))  # 返回格式化后的分数
        except:
            return "Error"  # 如果计算出错,返回"Error"

检查答案正确性

实施流程

flowchart TD Start(开始检查答案) --> OpenFiles[打开练习和答案文件] OpenFiles --> ReadExer[读取练习题目] OpenFiles --> ReadAns[读取用户答案] ReadExer --> CalcAns[calculated_answer] ReadAns --> GetAns[获取用户答案] GetAns --> Compare{比较答案} Compare -- 相同 --> Correct[记录正确答案] Compare -- 不同 --> Wrong[记录错误答案] Correct --> SaveGrade[保存成绩报告] Wrong --> SaveGrade SaveGrade --> CloseFiles[关闭文件] CloseFiles --> End(结束) classDef startend fill:#f96,stroke:#333,stroke-width:2px; classDef function fill:#9f6,stroke:#333,stroke-width:1px; class Start,End startend; class OpenFiles,ReadExer,ReadAns,CalcAns,GetAns,Compare,Correct,Wrong,SaveGrade,CloseFiles function;
# 检查答案的正确性并统计正确和错误的数量,输出到 Grade.txt 文件
    def check_answers(exercise_file, answer_file):
        correct_count = 0  # 正确数量
        wrong_count = 0  # 错误数量
        correct_indices = []  # 正确题目的索引
        wrong_indices = []  # 错误题目的索引
        answer_f = open(answer_file, 'r')  # 打开答案文件
        with open(exercise_file, 'r') as ef:  # 打开练习文件
            exercises = ef.readlines()  # 读取所有练习题目
            for i, exercise in enumerate(exercises, start=1):
                exercise_expr = exercise[:-1].split('=')[0]  # 获取表达式
                calculated_answer = evaluate_expression(exercise_expr)  # 计算答案
                answer_line = answer_f.readline().strip()  # 获取用户答案
                # 将答案转换为统一格式进行比较
                try:
                    if calculated_answer != "Error":
                        formatted_calculated_answer = Fraction(calculated_answer)  # 格式化计算答案
                    else:
                        formatted_calculated_answer = calculated_answer
                    if answer_line != "Error":
                        formatted_answer_line = Fraction(answer_line)  # 格式化用户答案
                    else:
                        formatted_answer_line = answer_line
                    if formatted_calculated_answer == formatted_answer_line:
                        correct_count += 1  # 如果答案相同,增加正确数量
                        correct_indices.append(i)  # 添加正确题目索引
                    else:
                        wrong_count += 1  # 如果答案不同,增加错误数量
                        wrong_indices.append(i)  # 添加错误题目索引
                except ValueError:
                    wrong_count += 1  # 如果转换出错,增加错误数量
                    wrong_indices.append(i)  # 添加错误题目索引
        answer_f.close()  # 关闭答案文件
        with open('Grade.txt', 'w') as gf:  # 打开成绩文件
            gf.write(f'Correct: {correct_count} ({", ".join(map(str, correct_indices))})\n')  # 写入正确信息
            gf.write(f'Wrong: {wrong_count} ({", ".join(map(str, wrong_indices))})\n')  # 写入错误信息

五、测试运行

  1. 测试用例 1:生成单个问题

    • 输入:num_questions=1, range_num=10
    • 预期:生成一个包含随机四则运算的题目。
  2. 测试用例 2:生成多个问题

    • 输入:num_questions=5, range_num=5
    • 预期:生成五个不重复的四则运算题目。
  3. 测试用例 3:包含所有运算符

    • 输入:num_questions=10, range_num=2
    • 预期:生成的题目中包含加、减、乘、除的所有运算符。
  4. 测试用例 4:检查答案功能

    • 输入: python main.py -e Exercises.txt -a Answers.txt
    • 预期:正确找出错误答案并给出题号
  5. 测试用例 5:数值范围为0(无效范围)

    • 输入: python main.py -n 10 -r 0
    • 预期:报错。
  6. 测试用例 6:题目数量为0

    • 输入: python main.py -n 0 -r 10
    • 预期:报错。
  7. 测试用例 7:题目数量为1.99(无效数量)

    • 输入:python main.py -n 1.99 -r 10
    • 预期:报错
  8. 测试用例 8:必要参数缺失

    • 输入:python main.py -n 10
    • 预期:程报错
  9. 测试用例 9:大量、大范围算式生成(性能测试)

    • 输入:用户答案文件包含错误答案。
    • 预期:程序正确标记错误答案并给出错误题目的索引。
  10. 测试用例 10:性能测试

    • 输入:python main.py -n 1000 -r 1000
    • 预期:程序在生成大量题目时仍能保持合理的响应时间。

六、项目小结

在我们的结对编程项目中,我们通过有效沟通、实时代码审查和相互学习,成功实现了这个项目。我们面临的挑战包括工作节奏的差异、解决冲突和时间管理,即使课程安排一直,但个人时间的分配很难统一,在多次的沟通和协商之下才能确定分工、需求分析和设计以及实施等。我们学到了准备的重要性、适应性的必要和持续反馈的价值。结对编程不仅增强了我们的技术能力,也加深了我们的友谊。我们互相赞赏对方的耐心、创新思维和对细节的关注,并建议未来项目中更多地使用自动化测试和代码重构。这次经历让我们坚信结对编程是一种宝贵的实践。

posted @ 2024-09-27 20:24  菜菜X  阅读(19)  评论(0编辑  收藏  举报