软件工程结对项目 3:python实现自动生成小学四则运算题目的程序

news/2024/9/19 3:48:02/文章来源:https://www.cnblogs.com/axunlu/p/18416767
这个作业属于哪个课程 广工计院计科34班软工
这个作业要求在哪里 作业要求
团队成员1 庄崇立3122004633
团队成员2 罗振烘3122004748
这个作业的目标 结对合作完成小学四则运算题目的程序,熟悉项目开发流程,提高团队合作能力

一、GitHub地址

二、需求

1.题目:实现一个自动生成小学四则运算题目的命令行程序(也可以用图像界面,具有相似功能)。
2.说明:
自然数:0, 1, 2, …。
真分数:1/2, 1/3, 2/3, 1/4, 1’1/2, …。
运算符:+, −, ×, ÷。
括号:(, )。
等号:=。
分隔符:空格(用于四则运算符和等号前后)。
算术表达式:
e = n | e1 + e2 | e1 − e2 | e1 × e2 | e1 ÷ e2 | (e),
其中e, e1和e2为表达式,n为自然数或真分数。
四则运算题目:e = ,其中e为算术表达式。
3.需求:
使用 -n 参数控制生成题目的个数,例如
Myapp.exe -n 10
将生成10个题目。
使用 -r 参数控制题目中数值(自然数、真分数和真分数分母)的范围,例如
Myapp.exe -r 10
将生成10以内(不包括10)的四则运算题目。该参数可以设置为1或其他自然数。该参数必须给定,否则程序报错并给出帮助信息。
生成的题目中计算过程不能产生负数,也就是说算术表达式中如果存在形如e1− e2的子表达式,那么e1≥ e2。
生成的题目中如果存在形如e1÷ e2的子表达式,那么其结果应是真分数。
每道题目中出现的运算符个数不超过3个。
程序一次运行生成的题目不能重复,即任何两道题目不能通过有限次交换+和×左右的算术表达式变换为同一道题目。例如,23 + 45 = 和45 + 23 = 是重复的题目,6 × 8 = 和8 × 6 = 也是重复的题目。3+(2+1)和1+2+3这两个题目是重复的,由于+是左结合的,1+2+3等价于(1+2)+3,也就是3+(1+2),也就是3+(2+1)。但是1+2+3和3+2+1是不重复的两道题,因为1+2+3等价于(1+2)+3,而3+2+1等价于(3+2)+1,它们之间不能通过有限次交换变成同一个题目。
生成的题目存入执行程序的当前目录下的Exercises.txt文件,格式如下:
四则运算题目1
四则运算题目2
……
其中真分数在输入输出时采用如下格式,真分数五分之三表示为3/5,真分数二又八分之三表示为2’3/8。
在生成题目的同时,计算出所有题目的答案,并存入执行程序的当前目录下的Answers.txt文件,格式如下:
答案1
答案2
特别的,真分数的运算如下例所示:1/6 + 1/8 = 7/24。
程序应能支持一万道题目的生成。
程序支持对给定的题目文件和答案文件,判定答案中的对错并进行数量统计,输入参数如下:
Myapp.exe -e .txt -a .txt
统计结果输出到文件Grade.txt,格式如下:
Correct: 5 (1, 3, 5, 7, 9)
Wrong: 5 (2, 4, 6, 8, 10)
其中“:”后面的数字5表示对/错的题目的数量,括号内的是对/错题目的编号。为简单起见,假设输入的题目都是按照顺序编号的符合规范的题目。

三、PSP

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

四、程序设计实现过程

1,框架设计

  • 这个项目采用了模块化的设计,将功能逻辑、用户界面和程序入口分离,形成了清晰的三层结构

1.1,核心功能模块(function.py):

  • CustomMathError 类:自定义异常类,用于处理特定的数学相关错误。
  • Number 类:表示一个数字,包括整数部分和分数部分。它能够生成随机数,并且可以简化分数。
  • Fraction 类:处理两个 Number 对象之间的分数运算,支持加减乘除。
  • Expression 类:生成和计算数学表达式,是整个程序的核心。

1.2,用户界面模块(interface.py):

  • MathQuizApp 类:创建图形用户界面,管理用户交互和显示结果。

1.3,主程序入口(main.py):

  • 创建 Tkinter 根窗口并启动应用。

1.4,三个模块之间的关系

  • main.py 作为程序入口,它导入并实例化 interface.py 中的 MathQuizApp 类。
  • MathQuizApp 类在生成题目时,会创建并使用 function.py 中的 Expression 类。
  • Expression 类内部使用 Number 和 Fraction 类来生成和计算表达式。

2,代码部分

  • 我们更深入地分析项目的关键代码

2.1,function.py 中的 Number 类

  • python核心代码如下
class Number:def __init__(self, max: int = None, nums: tuple = None):"""初始化一个Number对象:param max: 随机生成数字时的最大值:param nums: 用于直接指定数字的组成部分(整数,分子,分母)"""try:if nums:# 如果提供了nums元组,直接用它来初始化数字if isinstance(nums, tuple) and len(nums) == 3:if nums[2] == 0:raise ValueError("分母不能为零。")else:self.integer = int(nums[0])  # 整数部分self.numerator = int(nums[1])  # 分子self.denominator = int(nums[2])  # 分母else:raise ValueError("nums必须是一个三元组(整数,分子,分母)。")else:# 如果没有提供nums,则随机生成一个数字if (not isinstance(max, int)) or max < 1:raise ValueError("max必须大于等于1。")self.integer = random.randint(0, max-1)  # 随机生成整数部分self.denominator = random.randint(1, max-1)  # 随机生成分母is_fra = random.randint(1, 2)  # 随机决定是否生成分数部分if is_fra == 1:self.numerator = random.randint(1, self.denominator)  # 生成分子else:self.numerator = 0  # 不生成分数部分# 计算生成的数值generated_value = self.integer + self.numerator / self.denominator# 如果生成的值大于等于 max,调整整数部分使其小于 maxif generated_value >= max:adjustment = int(generated_value // max)self.integer -= adjustmentexcept ValueError as e:raise CustomMathError(str(e))def reduce_fraction(self):"""简化分数"""# 计算最大公约数gcd = math.gcd(self.numerator, self.denominator)numerator = self.numerator // gcddenominator = self.denominator // gcd# 处理假分数,将其转化为带分数integer = numerator // denominator + self.integernumerator = numerator % denominatorself.integer = int(integer)self.numerator = int(numerator)self.denominator = int(denominator)def __str__(self):"""将Number对象转换为字符串表示"""if self.integer == 0:if self.numerator:return f"{self.numerator}/{self.denominator}"else:return '0'else:if self.numerator:return f"{self.integer}'{self.numerator}/{self.denominator}"else:return str(self.integer)def __float__(self):"""将Number对象转换为浮点数"""return self.integer + self.numerator / self.denominator
  • Number 类的设计很巧妙:

    • 它可以通过 max 参数随机生成一个不超过 max 的数,也可以通过 nums 元组直接指定数值。
    • 生成的数字包含整数部分和分数部分,这样可以生成更多样的题目。
    • reduce_fraction 方法用于简化分数,保证生成的题目更加规范。
  • 整体流程图如下

2.2,function.py 中的 Expression 类

  • python核心代码如下
class Expression:def generate_expression_list(self):"""生成表达式列表"""expression_list = []sub_num = random.randint(1, 3)  # 随机决定子表达式的数量operators = [random.choice([' + ', ' - ', ' * ', ' % ']) for _ in range(sub_num)]subexpression_1 = self.generate_subexpression(1, operators[0])str_sub_1 = str(subexpression_1[0]) + subexpression_1[1] + str(subexpression_1[2])if sub_num == 1:expression_list.append(subexpression_1)str_sub_2 = ''str_sub_3 = ''string = str_sub_1 + '|' + str_sub_2 + '|' + str_sub_3elif sub_num == 2:subexpression_2 = self.generate_subexpression(2, operators[1])str_sub_2 = str(subexpression_2[0]) + str(subexpression_2[1])str_sub_3 = ''expression_list.append(subexpression_1)expression_list.append(subexpression_2)string = str_sub_1 + '|' + str_sub_2 + '|' + str_sub_3else:etype = random.randint(1, 2)subexpression_2 = self.generate_subexpression(etype, operators[1])if etype == 1:subexpression_3 = [operators[2]]str_sub_2 = str(subexpression_2[0]) + str(subexpression_2[1]) + str(subexpression_2[2])str_sub_3 = subexpression_3[0]if str_sub_1 >= str_sub_2 and operators[2] in [' + ', ' * ']:expression_list.append(subexpression_1)expression_list.append(subexpression_2)expression_list.append(subexpression_3)string = str_sub_1 + '|' + str_sub_2 + '|' + str_sub_3else:expression_list.append(subexpression_2)expression_list.append(subexpression_1)expression_list.append(subexpression_3)string = str_sub_1 + '|' + str_sub_2 + '|' + str_sub_3else:subexpression_3 = self.generate_subexpression(2, operators[2])str_sub_2 = str(subexpression_2[0]) + str(subexpression_2[1])str_sub_3 = str(subexpression_3[0]) + str(subexpression_3[1])expression_list.append(subexpression_1)expression_list.append(subexpression_2)expression_list.append(subexpression_3)string = str_sub_1 + '|' + str_sub_2 + '|' + str_sub_3return expression_list, string
  • 这个方法是生成表达式的核心:
    • 它随机决定生成1到3个子表达式。
    • 对于每个子表达式,它随机选择运算符,并调用 generate_subexpression 方法生成数字。
    • 它还处理了不同数量子表达式的组合方式,确保生成的表达式合理且多样。
  • 整体简化流程图如下

2.3,interface.py 中的 MathQuizApp 类

  • python核心代码如下
class MathQuizApp:def generate_questions(self):try:num_questions = self.validate_input(self.num_questions_entry.get())max_value = self.validate_input(self.max_value_entry.get())exp = Expression(max_value, num_questions)self.questions, self.answers = exp.run()self.questions_text.delete(1.0, tk.END)self.answers_text.delete(1.0, tk.END)for i, question in enumerate(self.questions, start=1):self.questions_text.insert(tk.END, f"{i}. {question}\n")with open("Exercises.txt", "w") as question_file:question_file.writelines(f"{q}\n" for q in self.questions)with open("Answer.txt", "w") as answer_file:answer_file.writelines(f"{a}\n" for a in self.answers)except ValueError as e:self.show_error(str(e))def check_answers(self):user_answers = self.answers_text.get(1.0, tk.END).splitlines()correct = []incorrect = []for i, (user_answer, correct_answer) in enumerate(zip(user_answers, self.answers), start=1):if user_answer.strip() == correct_answer.strip():correct.append(i)else:incorrect.append(i)self.results_text.delete(1.0, tk.END)self.results_text.insert(tk.END, f"正确题号: {', '.join(map(str, correct))}\n")self.results_text.insert(tk.END, f"错误题号: {', '.join(map(str, incorrect))}\n")with open("Grade.txt", "w") as grade_file:grade_file.write(f"Correct: {len(correct)} ({', '.join(map(str, correct))})\n")grade_file.write(f"Wrong: {len(incorrect)} ({', '.join(map(str, incorrect))})\n")
  • 这些方法展示了 GUI 的核心功能:
    • generate_questions 方法获取用户输入,使用 Expression 类生成题目,然后显示题目并保存到文件。
    • check_answers 方法比较用户答案和正确答案,显示结果并保存到文件。
  • 完整流程图如下

五、单元测试

1,测试部分概述

为保证该项目代码质量和正确运行,设计者编写了单元测试代码。本次单元测试使用了unittest框架实现对项目的单元测试。测试文件test_function.py内是对功能代码的测试,包含5条测试用例,测试文件test_interface.py内是针对用户图形化界面代码的测试,包含7条测试用例。总共12条测试用例

2,测试点

根据项目的代码整理出以下测试点

3,部分测试代码

test_function.py部分代码

import unittest
from function import Number, Fraction, Expression, CustomMathErrorclass TestMathFunctions(unittest.TestCase):def test_number_initialization(self):# Test 1: Initialize Number with max valuenum1 = Number(10)self.assertIsInstance(num1, Number)self.assertLessEqual(float(num1), 11)  # 检查是否小于等于 11# Test 2: Initialize Number with specific valuesnum2 = Number(nums=(3, 1, 2))self.assertEqual(str(num2), "3'1/2")

test_interface.py部分代码

import unittest
from unittest.mock import patch, MagicMock
import tkinter as tk
from interface import MathQuizAppclass TestMathQuizApp(unittest.TestCase):def setUp(self):self.root = tk.Tk()self.app = MathQuizApp(self.root)def tearDown(self):self.root.destroy()@patch('interface.Expression')def test_generate_questions(self, mock_expression):# 测试用例 1: 正常生成题目mock_expression.return_value.run.return_value = (["1 + 1", "2 * 2"], ["2", "4"])self.app.num_questions_entry.insert(0, "2")self.app.max_value_entry.insert(0, "10")self.app.generate_questions()self.assertEqual(self.app.questions_text.get("1.0", tk.END).strip(), "1. 1 + 1\n2. 2 * 2")def test_validate_input(self):# 测试用例 2: 有效输入self.assertEqual(self.app.validate_input("5"), 5)

4, 运行测试用例

12条测试用例全部通过


六、性能分析

在用户图形化界面输入生成10000条题目,对此过程进行性能分析



根据执行结果可以得知,执行时间最长的函数为generate_questions,执行时间为599ms

七、项目总结

1,项目分工:

庄崇立:负责大部分主体代码和测试代码的编写
罗振烘:负责小部分代码编写和博客的编写

2,项目收获

2.1,技术方面的收获

  • 对 Python 语言的掌握更加深入。在项目过程中,我们运用了 Python 的各种特性,和各种库函数,提升了编程能力。
  • 学会了如何设计和实现一个具有特定功能的程序。从需求分析到算法设计,再到代码实现和测试,我们经历了完整的软件开发流程,积累了宝贵的实践经验。
  • 了解了如何进行程序优化。为了提高程序的性能和效率,我们不断尝试不同的方法,如优化算法、减少不必要的计算等,增强了问题解决的能力。

2.2,团队协作方面的收获

  • 高效沟通至关重要。在项目中,我们通过频繁的交流,及时分享彼此的想法和进展,避免了误解和重复工作,确保项目顺利进行。
  • 优势互补能提升整体效率。我们两人在不同方面各有优势,通过合理分工,充分发挥各自的长处,大大提高了项目的开发速度和质量。
  • 学会了互相支持和鼓励。在遇到困难时,我们互相提供帮助和建议,增强了团队的凝聚力。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/798566.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

结构、权限修饰符、类

结构、权限修饰符、类 结构(struct:自定义的数据结构) struct student {// 成员变量int number;char name[100];void func(){ // 成员函数(方法)number++;} };// 1. 形参为引用 void func1(student &stu){stu.number = 2000;strcpy_s(stu.name, sizeof(stu.name…

Leetcode 2183. 统计可以被 K 整除的下标对数目

1.题目基本信息 1.1.题目描述 给你一个下标从 0 开始、长度为 n 的整数数组 nums 和一个整数 k ,返回满足下述条件的下标对 (i, j) 的数目:0 <= i < j <= n - 1 且 nums[i] * nums[j] 能被 k 整除。1.2.题目地址 https://leetcode.cn/problems/count-array-pairs-di…

贪心算法-找不重叠的区间段

1.说明 有N个区间片段,查找其中不重叠的片段最大个数。例如(6 8),(2 4),(3 5),(1 5),(5 9),(8 10)这6个片段中,不重叠的片段最大个数为3,分别为(2 4),(6 8),(8 10)。 2.解析 先按照起始位置从小到大进行排序,使用贪心算法使有效片段尽可能小,即结束位置更靠前…

Nuxt Kit 中的页面和路由管理

title: Nuxt Kit 中的页面和路由管理 date: 2024/9/17 updated: 2024/9/17 author: cmdragon excerpt: 摘要:本文介绍了Nuxt Kit中页面和路由管理的高级功能,包括extendPages自定义页面路由、extendRouteRules定义复杂路由逻辑及addRouteMiddleware注册路由中间件。通过这些…

堆的应用

1.需要具备的知识 1.1以顺序存储方式存储完全二叉树 完全二叉树:节点从上到下,从左到右布局的二叉树,如下图所示。完全二叉树可以使用类似数组这种顺序存储的结构存节点,如下图。按照"层级遍历"方式遍历这棵树(还有"前序、中序、后序"遍历方式,这里不做…

Oliver编译安装(Windows10+VisualStudio2022)

Oliver是一个开源的非线性视频编辑器。主要基于Qt和FFmpeg开发。前置条件 电脑上需要的环境Qt(>=5.15) VisualStudio(2022,其他版也可) vcpkg软件安装安装Qt5.15令人糟糕的是,Qt如今变得不太容易安装。自从Qt5.15以后的版本,就取消了离线安装。所有的Qt后序版本就只能通过…

CVE-2021-24762 复现

CVE-2021-24762 复现一看是个wordpress,看了下版本6.0没洞,直接扔wpscan扫一下 发现了个插件一搜发现perfect-survey在1.5.2之前都有洞,直接搜exp打. 上官网找了个API重扫一遍,直接给出了CVE号!直接找个sqlmap的exp跑一下,注意第二个位置选n来设置cookie sqlmap -u "http…

Java后端对前端的数据进行校验

首先,作为一名后端程序员,大家一定要记住:不要相信前端传来的数据,后端程序员仿佛是国家的边境出入局的工作人员,承担这最后的防线,必须尽可能的防止错误信息的流入导致正确信息的流出。因此,后端程序员必须要对前端传来的数据进行校验。这里呢,发现一个很好的校验工具…

vp介绍

想要更多请到:https://budingcat.xyz 注册了解详情 可以点击工单进行沟通哦~

云计算与大数据概论--金功勋

week4的 week5 Hadoop介绍起源:Hadoop as a solution:Building blocks:Namenodeif other nodes fail:DataNode:Block 1Secondary Namenode:JobTracker:if fails:Topolosy clusterPig:Hive :PIGweek10 week13:

通过vscode 创建uniapp项目

一、创建项目 uniapp官网教程: https://uniapp.dcloud.net.cn/quickstart-cli.html控制台命令: npx degit dcloudio/uni-preset-vue#vite <projectName> npm i二、使用vscode运行项目

Java多线程复习

目录3种创建方式(现阶段推荐 Runnable接口)下载网上的图片(利用了commons-io中的copyUrlToFiles方法)小结买票的例子(Thread的构造方法,获取当前线程的名称,线程休眠)龟兔赛跑的例子实现Callable接口线程停止线程休眠线程礼让Join方法(main线程与Thread子线程)线程状…