软件工程(结对编程项目)

Github作业链接

成员:刘鸿杰 林程星

软件工程 计科21级4班
作业要求 作业3要求连接
作业目标 实现四则运算题目的命令行程序

PSP表格

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

需求描述

题目:实现一个自动生成小学四则运算题目的命令行程序

  1. 使用 -n 参数控制生成题目的个数
  2. 使用 -r 参数控制题目中数值(自然数、真分数和真分数分母)的范围,该参数必须给定,否则程序报错并给出帮助信息
  3. 生成的题目中计算过程不能产生负数,也就是说算术表达式中如果存在形如e1− e2的子表达式,那么e1≥ e2
  4. 生成的题目中如果存在形如e1÷ e2的子表达式,那么其结果应是真分数
  5. 每道题目中出现的运算符个数不超过3个
  6. 程序一次运行生成的题目不能重复,即任何两道题目不能通过有限次交换+和×左右的算术表达式变换为同一道题目,生成的题目存入执行程序的当前目录下的Exercises.txt文件
  7. 在生成题目的同时,计算出所有题目的答案,并存入执行程序的当前目录下的Answers.txt文件
  8. 程序应能支持一万道题目的生成
  9. 程序支持对给定的题目文件和答案文件,判定答案中的对错并进行数量统计,统计结果输出到文件Grade.txt

需求分析

题目分析

  • 使用参数控制生成题目个数和数值范围

  • 作业要求生成四则运算式,运算符最多三个不能出现负数分数必须是真分数题目不能重复

  • 将生成的算式,答案,判题结果都存储到文件中,支持一万道题目生成

涉及到程序参数获取,随机数生成,字符串拼接,数值校验,文件I/O流操作等,本次作业采用python完成,使用到argparse参数解析,re正则匹配,random随机数生成,Fraction分数计算

思路分析

  1. 使用随机数生成运算符个数和操作数,同时判断运算符为减号时,不能出现负数;运算符为除号时,不能出现假分数;将操作数和运算符拼接,并在随机位置插入括号,这样就可生成算式
  2. 计算算式的值时,先将运算符转换为计算机能识别的运算符,使用正则表达式匹配操作数使用Fraction类将操作数分数化,便于结果的输出表示,再调用eval函数计算算式的答案
  3. 使用文件I/O流将算式和答案按要求的格式保存到指定文件中,当要校对答案时,将题目文件和答卷文件进行读取,将标准答案与输入答案比对,计算答对信息和答错信息保存到指定成绩文件中

流程分析

详细设计

  • 设计一个算式类Arithmetic,包含两个静态方法generate_arithmetic,calculate_arithmetic,分别用于生成算式,计算答案,在初始化方法里生成具体算式,对应答案,并赋值给formula,result属性
  • 为了保存算式和答案,读取算式和答案,保存成绩信息,设计write_exercises,write_answer,read_exercises,read_answer,write_grade五个函数
  • 设计exercises函数,实例化Arithmetic对象,根据题目数量和数值范围要求得到算式和答案,调用write_exercises,write_answer函数保存算式和答案
  • 设计correct_answer函数,调用read_exercises函数和read_answer函数读取题目文件和答卷文件,调用Arithmetic类的静态方法calculate_arithmetic来计算答案,与答卷文件中的答案比对,统计答对与答错的信息,调用write_grade函数将统计结果保存到指定文件中
  • 最后为了使用接收命令行参数实现功能,创建两个文件main_exercises和main_correct,使用argparse来解析命令行参数,然后分别调用moudle文件中的exercises函数和correct_answer函数,分开实现生成四则运算式和校对答案获取成绩信息两个大功能

关键代码

关键代码在于Arithmetic类,含有两个静态方法,generate_arithmetic和calculate_arithmetic,用于生成四则运算式和计算算式答案,主要运用到随机数生成,字符串拼接,正则表达式匹配等

# Arithmetic 算数类
class Arithmetic:
    def __init__(self, r):
        try:
            if not isinstance(r, int) or r < 0:
                raise TypeError
        except TypeError:
            print("输入的参数范围值必须是自然数")
        self.result = -1
        while self.result < 0:
            self.formula = self.generate_arithmetic(r)
            try:
                self.result = self.calculate_arithmetic(self.formula)
            except ZeroDivisionError:
                continue

    @staticmethod
    def generate_arithmetic(r):
        """
        :param r: 数字上限
        :return: 字符串:算式
        """
        num_operator = random.randint(1, 3)
        operator_list = []
        for _ in range(num_operator):
            idx = random.randint(0, 3)
            operator = '+-×÷'
            operator_list.append(operator[idx])
        operator_list.append('=')

        # 生成运算数
        number_list = []
        for i in range(num_operator + 1):
            number = random.randint(0, r)
            if i != 0:
                if operator_list[i - 1] == '-':
                    number = random.randint(0, int(number_list[i - 1]))
                elif operator_list[i - 1] == '÷':
                    if number_list[i - 1] == 0:
                        number = random.randint(1, r)
                    else:
                        number = random.randint(int(number_list[i - 1]), r)

            number_list.append(number)
        # 加括号
        idx_left_bracket = random.randint(0, len(number_list) - 2)

        arithmetic = []
        for i in range(len(number_list)):
            if i == idx_left_bracket and num_operator > 1:
                arithmetic.append('( ' + str(number_list[i]) + ' ' + operator_list[i] + ' ')
            elif i == idx_left_bracket + 1 and num_operator > 1:
                arithmetic.append(str(number_list[i]) + ' ) ' + operator_list[i] + ' ')
            else:
                arithmetic.append(str(number_list[i]) + ' ' + operator_list[i] + ' ')

        arithmetic = ''.join(arithmetic)

        return arithmetic

    @staticmethod
    def calculate_arithmetic(formula):
        """
        :param formula: string:算式
        :return: Fraction对象:答案
        """
        # 将符号替换成python能识别的运算符
        formula = formula.replace('=', '')
        formula = formula.replace('×', '*')
        formula = formula.replace('÷', '/')
        formula = formula.replace(' ', '')

        # 使用正则表达式将所有数字转换为Fraction类型,便于分数计算,再使用eval()函数计算字符串内的表达式
        res = eval(re.sub(r'(\d+)', r'Fraction(\1)', formula))

        return res

性能分析

性能分析主要包含对main_exercises.py和main_correct.py两个文件进行执行时间的分析,主要消耗在哪个函数或哪个模块,然后才能清楚性能瓶颈在哪,方便后期能够升级改进

  • 首先分析main_exercises.py文件,该文件main方法主要是进行命令函参数解析,然后调用moudle中exercises函数根据指定范围生成默认10道题目,然后将题目和答案写入文件中

通过分析发现时间主要花费在moudle文件中的exercises函数,指定生成10000道题目,花费时间为1279ms,花费时间主要在calculate_arithmetic方法上,为642ms,执行eval表达式计算答案花费其中443ms

  • 接着分析main_correct.py文件,该文件main方法主要是进行命令函参数解析,然后调用moudle中correct_answer函数读取题目文件和答卷文件,然后进行校对,统计答对和答错的信息,最后将成绩写入到指定文件中

通过分析发现时间主要花费在moudle文件中的correct_answer函数,测试时读取10000道题目和答案进行校对,且花费时间为569ms,花费时间主要在calculate_arithmetic函数,为404ms

测试运行

单元测试

为了对程序功能进行较为全面的测试,本次项目设计了10个测试用例,检测代码的健壮性,对程序实现进行较为全面的评估,从不同角度,不同函数进行测试分析

  1. test_arithmetic_parameters:测试生成算式时参数范围不是自然数

        # 测试生成算式时参数范围不是自然数
        def test_arithmetic_parameters(self):
            modules.Arithmetic(-2)
    
  2. test_generate_formular:测试生成10条算式是否符合要求

    # 测试生成10条算式是否符合要求
        def test_generate_formular(self):
            for _ in range(10):
                formular = modules.Arithmetic.generate_arithmetic(8)
                print(formular)
    
  3. test_generate_enough_formular:测试能否生成一万条算式

    # 测试能否生成一万条算式
        def test_generate_enough_formular(self):
            for _ in range(10000):
                formular = modules.Arithmetic.generate_arithmetic(10)
                print(formular)
    
  4. test_answer_express:测试文件内容分词效果

    # 测试计算的答案表示是否符合要求
        def test_answer_express(self):
            formular = '3 + 6 ÷ 7 ='
            answer = modules.Arithmetic.calculate_arithmetic(formular)
            print(formular, "", answer)
    
  5. test_formular_answer:测试生成算式后计算的答案是否正确

    # 测试生成算式后计算的答案是否正确
        def test_formular_answer(self):
            for _ in range(10):
                formular = modules.Arithmetic.generate_arithmetic(8)
                answer = modules.Arithmetic.calculate_arithmetic(formular)
                print(formular, " ", answer)
    
  6. test_write_file:测试将生成的题目和答案写入指定文件

    # 测试将生成的题目和答案写入指定文件
        def test_write_file(self):
            formular_list = []
            answer_list = []
            for _ in range(10):
                arithmetic = modules.Arithmetic(10)
                formular_list.append(arithmetic.formula)
                answer_list.append(arithmetic.result)
            modules.write_exercises(formular_list)
            modules.write_answer(answer_list)
    
  7. test_read_not_exist_file:测试读取的题目文件和答案文件不存在

    # 测试读取的题目文件和答案文件不存在
        def test_read_not_exist_file(self):
            formular_list = modules.read_exercises("train.txt")
            answer_list = modules.read_answer("result.txt")
            print(formular_list)
            print(answer_list)
    
  8. test_read_file:测试将题目文件和答案文件进行读取

    # 测试将题目文件和答案文件进行读取
        def test_read_file(self):
            formular_list = modules.read_exercises("Exercises.txt")
            answer_list = modules.read_answer("Answer.txt")
            print(formular_list)
            print(answer_list)
    
  9. test_exercises:测试生成算式和计算相应答案,并写入到指定文件

    # 测试生成算式和计算相应答案,并写入到指定文件
        def test_exercises(self):
            modules.exercises(10, 10)
    
  10. test_correct_answer:测试校对答案

    # 测试校对答案
        def test_correct_answer(self):
            modules.correct_answer("Exercises.txt", "Answer.txt")
    

测试结果

10个单元测试全部执行成功,全部花费时间为11.608s,其中test_generate_enough_formular单元测试为生成一万条算式,花费时间为11.579s

代码覆盖率

由于在文件I/O流等部分需要捕获处理异常,所以代码覆盖率为以上所显示

项目总结

  1. 结对编程体现了沟通交流,思想灵感碰撞的魅力与无限创新可能,在项目中互相讨论,提出自己的思路见解,不断完善设计规划,锻炼了沟通的能力,体现了协作交流的重要性
  2. 结对编程体现了合作的关键性,在项目经过需求分析和详细设计后,要进入真正实现的过程,对于程序的推进,需要明确各自的目标,尽可能达到最优的实现方案,完成负责的模块,提高开发效率
  3. 为完成项目需求,需要分析具体的实现方式来解决遇到的难题,学习新的思维模式,采用相关技术克服难题,在这个过程锻炼了逻辑思维,提升了编程能力
posted @ 2023-09-25 20:27  _Lhj  阅读(89)  评论(0)    收藏  举报