这个作业属于哪个课程 | https://edu.cnblogs.com/campus/gdgy/CSGrade22-34 |
---|---|
这个作业要求在哪里 | https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/homework/13230 |
这个作业的目标 | 通过实现一个自动生成小学四则运算题目的命令行程序提高软件开发能力 |
姓名 | 李佳聪 |
学号 | 3222004509 |
github链接 | https://github.com/Ljcgiant/Ljcgiant/tree/main/3222004509 |
- psp表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 20 | 30 |
· Estimate | · 估计这个任务需要多少时间 | 20 | 20 |
Development | 开发 | 200 | 300 |
· Analysis | · 需求分析 (包括学习新技术) | 30 | 30 |
· Design Spec | · 生成设计文档 | 30 | 20 |
· Design Review | 设计复审 | 10 | 30 |
· Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 5 | 15 |
· Design | · 具体设计 | 40 | 60 |
· Coding | · 具体编码 | 720 | 1300 |
· Code Review | · 代码复审 | 180 | 240 |
· Test | · 测试(自我测试,修改代码,提交修改) | 200 | 480 |
Reporting | 报告 | 60 | 60 |
· Test Repor | · 测试报告 | 10 | 20 |
· Size Measurement | · 计算工作量 | 5 | 10 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 10 | 15 |
· 合计 | 2065 | 3185 |
-
设计与实现过程
2.1 数据结构设计
题目:包含运算数和运算符的字符串。
答案:每个题目对应的正确答案,可以是整数或分数。
2.2 算法设计
随机数生成:生成指定范围内的随机整数或真分数。
表达式生成:根据随机数和运算符生成单个表达式。
题目生成:组合多个表达式生成完整的题目。
答案计算:计算题目的答案。
文件读写:将题目和答案保存到文件中,以及从文件中读取题目和答案进行验证。
2.3 功能模块设计
命令行参数解析:处理用户输入。
题目生成模块:生成题目和答案。
答案验证模块:检查答案文件中的答案是否正确。
记录模块:记录并保存成绩。
2.4 实现
使用 argparse 库解析命令行参数。
generate_number:生成随机数。
generate_expression:生成包含两个数和运算符的表达式。
generate_question:生成一个完整的题目。
generate_questions:生成指定数量的题目。
calculate_answer:计算题目的答案。
validate_answers:验证答案文件中的答案是否与计算出的答案一致。
save_grades:保存到文件中
2.4 关键函数流程图
generate_expression:
generate_questions:
validate_answers:
save_grades:
-
效能分析
-
代码说明
4.1 解析命令行参数
点击查看代码
def parse_arguments():
parser = argparse.ArgumentParser(description="Generate arithmetic questions and validate answers.")
parser.add_argument("-n", type=int, help="Number of questions to generate")
parser.add_argument("-r", type=int, required=True, help="Range of numbers")
parser.add_argument("-e", type=str, help="Exercise file to validate")
parser.add_argument("-a", type=str, help="Answer file to validate")
return parser.parse_args()
这个函数使用argparse库来解析命令行参数。它定义了四个参数:
"-n": 生成问题的数目。
"-r": 数字的范围,是必需的。
"-e": 要验证的练习文件。
"-a": 要验证的答案文件。
4.2 生成随机数
点击查看代码
def generate_number(max_range):
if random.random() < 0.5:
return random.randint(1, max_range - 1)
else:
numerator = random.randint(1, max_range - 1)
denominator = random.randint(1, max_range - 1)
while numerator >= denominator:
numerator = random.randint(1, max_range - 1)
denominator = random.randint(1, max_range - 1)
return Fraction(numerator, denominator)
这个函数生成一个随机数,要么是一个整数,要么是一个真分数(分子小于分母的分数)。如果随机数小于0.5,则生成一个整数;否则生成一个真分数。
4.3 生成算术表达式
点击查看代码
def generate_expression(max_range):
num_count = random.randint(2, 4)
nums = [generate_number(max_range) for _ in range(num_count)]
operators = ['+', '-', '*', '/']
expression = str(nums[0])
for i, num in enumerate(nums[1:], start=1):
operator = random.choice(operators)
if operator == '-' and nums[i-1] < num:
nums[i-1], num = num, nums[i-1]
elif operator == '/':
if nums[i-1] > num:
nums[i-1], num = num, nums[i-1]
else:
continue
expression += f" {operator} {num}"
return expression
这个函数生成一个算术表达式,包含2到4个随机数和随机选择的运算符(加、减、乘、除)。它确保减法的结果非负,除法的除数小于被除数。
4.4 生成问题
点击查看代码
def generate_question(max_range):
expression = generate_expression(max_range)
return f"{expression} "
这个函数生成一个问题,即一个算术表达式。
4.5 生成多个问题
点击查看代码
def generate_questions(num_questions, max_range):
questions = set()
while len(questions) < num_questions:
question = generate_question(max_range)
questions.add(question)
return list(questions)
这个函数生成指定数量的问题,确保每个问题都是唯一的。
4.6 计算答案
点击查看代码
def calculate_answer(expression):
try:
answer = sympify(expression)
float_answer = float(answer.evalf())
return int(float_answer) if float_answer.is_integer() else float_answer
except Exception as e:
print(f"Error calculating answer: {e}")
return e
这个函数使用sympy库来计算算术表达式的答案。如果表达式计算成功,返回整数或浮点数;如果失败,返回错误。
4.7 保存问题和答案
点击查看代码
def save_questions(questions, filename="Exercises.txt"):
with open(filename, "w") as file:
for idx, question in enumerate(questions, 1):
file.write(f"四则运算题目{idx}\n{question} = \n")
def save_answers(answers, filename="Answers.txt"):
with open(filename, "w") as file:
for idx, answer in enumerate(answers, 1):
file.write(f"{answer}\n")
这两个函数分别用于将生成的问题和答案保存到文件中。
4.8 验证答案
点击查看代码
def validate_answers(exercise_file, answer_file):
correct = []
wrong = []
question_number = 1
with open(exercise_file, "r") as ex_file, open(answer_file, "r") as ans_file:
for ex_line, ans_line in zip(ex_file, ans_file):
exercise = ex_line.strip()
try:
answer = Fraction(ans_line.strip())
calculated_answer = calculate_answer(exercise)
if calculated_answer is None:
raise ValueError(f"calculate_answer returned None for expression '{exercise}'")
if calculated_answer == answer:
correct.append(f"四则运算题目{question_number}")
else:
wrong.append(f"四则运算题目{question_number}")
question_number += 1
except ValueError as e:
print(f"{e} in line: {ans_line.strip()}")
wrong.append(f"四则运算题目{question_number}")
question_number += 1
except Exception as e:
print(f"Error calculating answer for: {exercise}. Error: {e}")
wrong.append(f"四则运算题目{question_number}")
question_number += 1
return correct, wrong
这个函数验证答案文件中的答案是否正确,并记录正确和错误的答案。
4.9 保存到Grade.txt
点击查看代码
def save_grades(correct, wrong, filename="Grade.txt"):
with open(filename, "w") as file:
correct_ids = [idx.split("题目")[1] for idx in correct]
wrong_ids = [idx.split("题目")[1] for idx in wrong]
file.write(f"Correct: {len(correct)} ({', '.join(correct_ids)})\n")
file.write(f"Wrong: {len(wrong)} ({', '.join(wrong_ids)})\n")
这个函数将正确和错误的答案编号保存到成绩文件中。
4.10 主函数
点击查看代码
def main():
args = parse_arguments()
if args.n:
max_range = 10 if args.r >= 10 else args.r
questions = generate_questions(args.n, max_range)
answers = [calculate_answer(q) for q in questions]
save_questions(questions, "Exercises.txt")
save_answers(answers, "Answers.txt")
if args.e and args.a:
print(f"Validating answers using exercise file: {args.e}")
print(f"And answer file: {args.a}")
correct, wrong = validate_answers(args.e, args.a)
print(f"Correct answers: {correct}")
print(f"Wrong answers: {wrong}")
save_grades(correct, wrong, "Grade.txt")
print("Grades have been saved.")
-
测试运行
-
项目小结
这次的经验教训是要充分考虑边界条件,如空列表输入;确保函数返回类型与预期一致;性能测试应涵盖实际运行环境等。总结说来,算法设计需平衡时间与空间效率,通过测试揭示问题并优化,以达到高效、稳定的性能表现。