这个作业属于哪个课程 | https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/ |
---|---|
这个作业要求在哪里 | 结对项目 - 作业 - 计科22级34班 - 班级博客 - 博客园 (cnblogs.com) |
这个作业的目标 | 结对项目——实现一个自动生成小学四则运算题目的命令行程序 |
成员1 | 陈奕奕 3222004552 |
成员2 | 林闰埼 3222004595 |
Only-a-nose/PairWork (github.com)
1. PSP2.1表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 600 | 580 |
· Estimate | · 估计这个任务需要多少时间 | 600 | 580 |
Development | 开发 | 580 | 550 |
· Analysis | · 需求分析 (包括学习新技术) | 120 | 100 |
· Design Spec | · 生成设计文档 | 40 | 40 |
· Design Review | · 设计复审 (和同事审核设计文档) | 40 | 40 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 40 | 50 |
· Design | · 具体设计 | 100 | 90 |
· Coding | · 具体编码 | 120 | 130 |
· Code Review | · 代码复审 | 60 | 50 |
· Test | · 测试(自我测试,修改代码,提交修改) | 60 | 50 |
Reporting | 报告 | 70 | 90 |
· Test Report | · 测试报告 | 25 | 30 |
· Size Measurement | · 计算工作量 | 20 | 30 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 25 | 30 |
合计 | 1250 | 1220 |
2.性能分析
使用 cProfile
进行性能分析
cProfile
是 Python 内置的性能分析器,能够记录函数的调用次数和执行时间,帮助发现性能瓶颈。
-
使用
cProfile
分析整个程序
在命令行运行程序时,使用 cProfile
来分析程序的性能,命令如下:
python -m cProfile -s time D:\pythonProject1\Software_Engineer\MyApp.py -n 10 -r 10
-
-s time
:按执行时间对结果进行排序,找到执行时间最长的函数。 -
MyApp.py
后跟的参数依然是程序的命令行参数,比如-n
是生成题目个数,-r
是数值范围。 -
分析结果
cProfile
的输出将显示每个函数的以下内容:
- ncalls:调用的次数。
- tottime:该函数中不包括子函数的总执行时间。
- percall:每次调用该函数的平均时间(
tottime
除以ncalls
)。 - cumtime:该函数及其子函数的累计执行时间。
- percall (cumtime):每次调用该函数的累计时间(
cumtime
除以ncalls
)。
-
将性能分析结果保存为文件
可以将 cProfile
的结果输出到文件进行进一步分析:
python -m cProfile -o output.pstats D:\pythonProject1\Software_Engineer\MyApp.py -n 10 -r 10
-
使用
pstats
模块对文件中的数据进行查看和排序
import pstats
p = pstats.Stats('C:\\Users\\陈奕奕\\output.pstats')
p.strip_dirs().sort_stats('time').print_stats(10) # 打印执行时间最长的前10个函数
3.设计实现过程
1. 程序概述
本程序旨在生成小学四则运算题目,并且支持对用户的答案文件进行批改。程序通过命令行参数控制生成题目的数量、数值范围,以及判定答案的功能。生成的题目和答案分别存储在 Exercises.txt
和 Answers.txt
文件中,判定结果保存在 Grade.txt
文件中。
2. 代码组织结构
程序主要由四个功能模块组成:
- 生成题目:生成符合条件的四则运算表达式。
- 计算答案:计算生成题目的答案。
- 判定对错:根据用户提供的答案文件与程序计算的答案进行比较,输出批改结果。
- 命令行参数解析:解析用户通过命令行输入的参数,调用不同的功能。
3. 关键函数设计
-
generate_question(range_value, operator_count=3)
- 功能:根据给定数值范围
range_value
生成四则运算题目。 - 输入:数值范围
range_value
和运算符个数operator_count
。 - 输出:返回一个运算表达式,包含自然数或真分数,以及最多 3 个运算符。
- 逻辑:
- 随机生成自然数或真分数作为操作数。
- 随机选择运算符。
- 拼接操作数和运算符,生成一个完整的四则运算表达式。
- 功能:根据给定数值范围
-
calculate_answer(expression)
- 功能:根据生成的表达式计算答案。
- 输入:四则运算表达式
expression
。 - 输出:返回计算结果的真分数格式,或者在除零时返回 "undefined"。
- 逻辑:
- 使用 Python 的
eval()
计算表达式。 - 捕捉除零异常,并输出 "undefined"。
- 使用 Python 的
-
check_answers(exercise_file, answer_file)
- 功能:检查用户提供的答案文件,判定每道题目对错。
- 输入:题目文件路径
exercise_file
和答案文件路径answer_file
。 - 输出:生成一个包含判定结果的
Grade.txt
文件。 - 逻辑:
- 读取题目和答案文件,按行处理。
- 逐行比较程序计算的答案和用户提供的答案,统计正确与错误的题目。
- 将结果写入
Grade.txt
。
-
generate_exercises_and_answers(n, range_value)
- 功能:生成
n
道题目并计算答案,存入Exercises.txt
和Answers.txt
文件。 - 输入:题目个数
n
和数值范围range_value
。 - 输出:生成的题目和答案分别存入两个文件中。
- 功能:生成
-
main()
- 功能:命令行解析,控制程序的整体逻辑。
- 逻辑:
- 如果传入
-n
和-r
参数,生成指定数量的题目和答案。 - 如果传入
-e
和-a
参数,进行答案对错判定。 - 如果参数不全,打印帮助信息。
- 如果传入
4. 模块之间的关系
- 生成题目模块(
generate_question()
)会调用生成随机数和运算符,最终返回一个四则运算表达式。 - 计算答案模块(
calculate_answer()
)会对生成的四则运算表达式进行计算,并将结果以真分数的形式返回。 - 判定对错模块(
check_answers()
)读取生成的题目和用户提供的答案进行比较,统计对错并输出到文件。 - 命令行解析模块(
main()
)控制程序的主流程,解析参数决定调用生成题目或判定答案的功能。
5. 关键流程图
1. generate_question()
函数流程图
2. calculate_answer()
函数流程图
3. check_answers()
函数流程图
6. 总结与改进
- 该程序的设计简洁,使用了模块化设计,方便各个功能的扩展。
- 使用命令行参数控制生成题目和判定答案功能,使得程序灵活性较强。
- 在未来的改进中,可以增加更多的输入校验功能,如防止用户输入无效参数,或者确保用户提供的答案文件格式正确。
4.代码说明
1. 模块导入
import argparse
import random
from fractions import Fraction
argparse
:用于处理命令行参数。random
:用于生成随机数,帮助创建随机四则运算题目。fractions.Fraction
:用于处理真分数,使得生成题目时可以包含分数运算。
2. 生成四则运算题目的函数:generate_question()
def generate_question(range_value, operator_count=3):operators = ['+', '-', '*', '/']numbers = []for _ in range(operator_count + 1):if random.choice([True, False]):numerator = random.randint(1, range_value - 1)denominator = random.randint(2, range_value)fraction = Fraction(numerator, denominator)numbers.append(fraction)else:numbers.append(random.randint(1, range_value - 1))expression = str(numbers[0])for i in range(operator_count):op = random.choice(operators)expression += f" {op} {numbers[i + 1]}"return expression
- 功能:随机生成包含真分数和自然数的四则运算题目。
- 参数:
range_value
:控制生成的随机数的范围。operator_count
:控制运算符的数量,默认是3个运算符。
- 流程:
- 随机选择数字,可以是自然数或真分数。
- 在数字之间插入随机运算符(
+
,-
,*
,/
)。 - 返回完整的四则运算表达式字符串。
3. 计算表达式结果的函数:calculate_answer()
def calculate_answer(expression):try:answer = eval(expression)return str(Fraction(answer).limit_denominator())except ZeroDivisionError:return "undefined"except Exception as e:return f"Error: {e}"
- 功能:计算四则运算题目的正确答案。
- 参数:
expression
是待计算的数学表达式字符串。 - 流程:
- 使用 Python 的内置
eval()
函数计算表达式的值。 - 将计算结果转换为最简分数形式。
- 如果遇到除以零错误,返回
"undefined"
。 - 处理其他异常并返回错误信息。
- 使用 Python 的内置
4. 判定答案对错的函数:check_answers()
def check_answers(exercise_file, answer_file):with open(exercise_file, "r") as ef, open(answer_file, "r") as af, open("Grade.txt", "w") as grade_file:correct = []wrong = []exercises = ef.readlines()answers = af.readlines()for i in range(len(exercises)):exercise = exercises[i].split('=')[0].split('. ')[1].strip()expected_answer = calculate_answer(exercise)given_answer = answers[i].split(". ")[1].strip()if expected_answer == given_answer:correct.append(i + 1)else:wrong.append(i + 1)grade_file.write(f"Correct: {len(correct)} ({', '.join(map(str, correct))})\n")grade_file.write(f"Wrong: {len(wrong)} ({', '.join(map(str, wrong))})\n")
- 功能:检查学生的答案是否正确,并将结果写入
Grade.txt
文件。 - 参数:
exercise_file
:题目文件的路径。answer_file
:答案文件的路径。
- 流程:
- 从题目文件和答案文件中读取数据。
- 对每个题目,提取出数学表达式并计算正确答案。
- 比较计算的答案和给出的答案。
- 记录正确和错误的题目编号,并将结果写入
Grade.txt
文件。
5. 生成题目和答案的函数:generate_exercises_and_answers()
def generate_exercises_and_answers(n, range_value):with open("Exercises.txt", "w") as exercise_file, open("Answers.txt", "w") as answer_file:for i in range(1, n + 1):question = generate_question(range_value)answer = calculate_answer(question)exercise_file.write(f"{i}. {question} =\n")answer_file.write(f"{i}. {answer}\n")
- 功能:生成题目和对应的答案,分别写入
Exercises.txt
和Answers.txt
文件中。 - 参数:
n
:题目的数量。range_value
:控制生成的随机数的范围。
- 流程:
- 生成指定数量的四则运算题目。
- 计算每道题目的正确答案。
- 将题目和答案分别写入两个文件中。
6. 主函数:main()
def main():parser = argparse.ArgumentParser(description="四则运算生成器")parser.add_argument("-n", type=int, help="生成题目的个数")parser.add_argument("-r", type=int, help="题目中数值的范围")parser.add_argument("-e", type=str, help="题目文件路径")parser.add_argument("-a", type=str, help="答案文件路径")args = parser.parse_args()if args.n is not None and args.r is not None:generate_exercises_and_answers(args.n, args.r)print(f"生成了 {args.n} 道题目,数值范围为 0 到 {args.r}")elif args.e and args.a:check_answers(args.e, args.a)print(f"判定答案对错,结果已存入 Grade.txt 文件。")else:parser.print_help()
- 功能:处理命令行参数,调用相应的功能。
- 参数说明:
-n
:生成题目的数量。-r
:控制生成题目的数值范围。-e
和-a
:分别指定题目文件和答案文件的路径,用于判定答案对错。
- 流程:
- 如果提供了
-n
和-r
参数,生成题目和答案文件。 - 如果提供了
-e
和-a
参数,检查给出的答案是否正确。 - 如果参数不完整,显示帮助信息。
- 如果提供了
7. 程序入口
if __name__ == "__main__":main()
- 功能:这是标准的 Python 程序入口,确保当文件作为脚本运行时调用
main()
函数。
5.测试运行
-
生成10道题目,数值范围在10以内:
python MyApp.py -n 10 -r 10
-
检查题目和答案文件的对错:
python MyApp.py -e Exercises.txt -a Answers.txt
-
命令窗口:
-
运行结果:
-
Exercises.txt
-
-
Answers.txt
-
Grade.txt
6.项目小结
1. 项目背景与目标
在这个结对项目中,我们的目标是开发一个四则运算题目生成器,并实现一个自动化的答案判定系统。该系统需要支持生成随机的四则运算题目(包括自然数和真分数),计算这些题目的正确答案,并对给出的答案进行准确的判定。项目的关键任务包括编写代码来生成题目、计算答案、检查答案的正确性,以及处理命令行参数以实现这些功能。
2. 成功经验
1. 任务分配与协作
我们在项目初期对任务进行了合理的分配。一方负责设计和实现题目生成和答案计算的核心逻辑,另一方则专注于答案的判定和命令行接口的实现。这种分工有效提高了工作效率,使得每个人可以专注于自己擅长的领域。
2. 测试与调试
我们制定了详尽的测试计划,包括单元测试和集成测试。通过 PyCharm 和命令行进行性能分析和测试,及时发现了潜在的问题并进行了修复。这种方法确保了代码的稳定性和准确性。
3. 文档与说明
我们在代码中添加了详细的注释和文档说明,解释了每个函数的功能和参数。这不仅帮助我们自己在后续维护时更容易理解代码,也使得项目对其他开发者更友好。
3. 遇到的挑战与教训
1. 异常处理
在处理四则运算题目的计算时,我们遇到了除以零的错误。最初没有考虑到这一点,导致了程序崩溃。经过调整,我们加入了专门的异常处理机制,以确保程序在遇到除以零的情况时能够返回 "undefined"
,而不是崩溃。
2. 参数处理
最开始对命令行参数的处理不够完善,导致在不同环境下运行时出现了参数解析错误。通过调试和修正,我们改善了参数处理的代码,确保了命令行工具的稳定性和可靠性。
3. 文件路径问题
在读取文件路径时,遇到了 Unicode 编码问题。通过使用原始字符串(r'path'
)来处理路径,我们解决了这个问题,并确保代码在不同操作系统上能够正常运行。
4. 结对感受
合作中的闪光点
- 高效的沟通:我们在项目中保持了频繁而有效的沟通。每当遇到问题时,我们都能够迅速协作找到解决方案。这种高效的沟通大大提高了项目的推进速度。
- 互补的技能:我们两个人在技能上互补,一方擅长逻辑和算法设计,另一方则精通命令行工具和文件操作。这种互补让我们的项目在各方面都能达到较高的质量。
建议
- 定期代码审查:虽然我们在项目中保持了良好的沟通,但定期的代码审查可以帮助更早地发现潜在问题。建议在未来的项目中加入更多的代码审查环节。
- 增强文档编写:虽然项目中有详细的注释和文档,但在复杂的功能实现中,提供更多的技术文档和使用指南会更有助于项目的维护和扩展。
5. 总结
通过这次结对项目,我们不仅完成了四则运算题目生成器和答案判定系统的开发任务,还在实践中积累了宝贵的经验。项目中的挑战和问题让我们认识到在开发过程中异常处理和参数管理的重要性,同时也让我们意识到团队合作和技能互补的优势。未来,我们将继续优化我们的工作方法,以确保项目的高效和稳定运行。
博文撰写者: 陈奕奕 和 林闰埼