首页 > 其他分享 >结对项目

结对项目

时间:2023-09-25 21:49:20浏览次数:40  
标签:结对 题目 项目 生成 fraction test expression generate

信息

姓名 学号 GitHub地址
钟学 3121005280 https://github.com/GTzx/project_2
陈勇佳 3121005292

软件工程 课程主页
作业要求 作业要求
作业目标 实现一个自动生成小学四则运算题目的命令行程序

需求

题目:实现一个自动生成小学四则运算题目的命令行程序(也可以用图像界面,具有相似功能)。

  1. 使用 -n 参数控制生成题目的个数,例如 Myapp.exe -n 10 将生成10个题目。

  2. 使用 -r 参数控制题目中数值(自然数、真分数和真分数分母)的范围,例如Myapp.exe -r 10
    将生成10以内(不包括10)的四则运算题目。该参数可以设置为1或其他自然数。该参数必须给定,否 则程序报错并给出帮助信息。

  3. 生成的题目中计算过程不能产生负数,也就是说算术表达式中如果存在形如e1− e2的子表达式,那么e1≥ e2。

  4. 生成的题目中如果存在形如e1÷ e2的子表达式,那么其结果应是真分数。

  5. 每道题目中出现的运算符个数不超过3个。

  6. 程序一次运行生成的题目不能重复,即任何两道题目不能通过有限次交换+和×左右的算术表达式变换为同一道题目。例如,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,它们之间不能通过有限次交换变成同一个题目。

  7. 生成的题目存入执行程序的当前目录下的Exercises.txt文件,格式如下:
    四则运算题目1
    四则运算题目2
    ……
    其中真分数在输入输出时采用如下格式,真分数五分之三表示为3/5,真分数二又八分之三表示为2’3/8。
    在生成题目的同时,计算出所有题目的答案,并存入执行程序的当前目录下的Answers.txt文件,格式如下:
    答案1
    答案2
    ……
    特别的,真分数的运算如下例所示:1/6 + 1/8 = 7/24。

  8. 程序应能支持一万道题目的生成。

  9. 程序支持对给定的题目文件和答案文件,判定答案中的对错并进行数量统计,输入参数如下:
    Myapp.exe -e [exercisefile.txt] -a [answerfile.txt]
    统计结果输出到文件Grade.txt,格式如下:
    Correct: 5 (1, 3, 5, 7, 9)
    Wrong: 5 (2, 4, 6, 8, 10)
    其中“:”后面的数字5表示对/错的题目的数量,括号内的是对/错题目的编号。为简单起见,假设输入的题目都是按照顺序编号的符合规范的题目。

模块设计

此项目设计了4个主要模块以用来完成题目要求。

  • generate_number ()是生成随机数函数,用来生成指定范围的自然数或真分数。

  • generate_expression() 是生成表达式的函数,用来生成满足题目要求的四则表达式。其中该模块还包含三个函数,即生成由一个运算符,两个运算符,三个运算符组成的表达式,分别为generate_one_expression() generate_two_expression() generate_three_expression()

  • generate_questions_and_answers ()是生成题目和答案的函数,把生成的题目和答案保存在列表中以确保后续方便读取。grade_questions ()是统计正确答案数量的函数。

  • 除了这几个主要模块,还有一些用于服务其他模块的函数,例如用来把假分数转换为带分数的convert_fraction (),保存到文件里的函数save_to_file(),这里不展示其详细代码。

生成随机数模块

  通过random库中的randint函数生成指定范围内的随机数(自然数或真分数)

# 生成随机数
def generate_number(range_limit):
    if random.random() < 0.5:  # 50% 的概率生成真分数
        numerator = random.randint(0, range_limit - 1)
        denominator = random.randint(numerator + 1, range_limit)
        return Fraction(numerator, denominator)
    else:  # 50% 的概率生成自然数
        return random.randint(0, range_limit - 1)

生成表达式模块

  随机生成一个运算符个数不超过3个的表达式,通过生成数和运算符,再通过题目要求修改后将数和运算符组成一个表达式字符串。

def generate_expression(range_limit):
    if range_limit < 1:
        raise ValueError("数值范围必须大于等于1")

    while True:
        yunsuanfu_num = random.randint(1, 3)

        if yunsuanfu_num == 1:
            expression, expression_1 = generate_one_expression(range_limit)
        elif yunsuanfu_num == 2:
            expression, expression_1 = generate_two_expression(range_limit)
        else:
            expression, expression_1 = generate_three_expression(range_limit)

        # 检查是否已生成过这个表达式,如果是则重新生成
        if expression not in generated_expressions:
            generated_expressions.add(expression)
            return expression, expression_1

生成题目和答案模块

  将已生成的表达式字符串进行预处理,修改为可运行的运算符,再通过eval函数计算除答案,并将题目和答案保存在相应的列表中。

# 生成题目和答案
def generate_questions_and_answers(num_questions, range_limit):
    questions = []
    answers = []
    for _ in range(num_questions):
        expression, expression_1 = generate_expression(range_limit)
        decimal_result = eval(expression_1.replace('÷', '/').replace('×', '*'))
        # while decimal_result < 0:
        #     expression, expression_1 = generate_expression(range_limit)
        #     decimal_result = eval(expression_1.replace('÷', '/').replace('×', '*'))
        fraction_result = convert_to_fraction(decimal_result)
        if isinstance(fraction_result, int) == False and fraction_result % 1 != 0:
            fraction_result = convert_fraction(f"{Fraction(fraction_result).limit_denominator()}")
        questions.append(expression)
        answers.append(fraction_result)
    return questions, answers

统计对错题目模块

  输入题目和答案文件,对其预处理后计算题目的答案是否与所给的答案文件里的答案一致,统计对错个数。

# 统计对错题目
def grade_questions(exercise_file, answer_file):
    correct_indices = []
    wrong_indices = []

    try:
        with open(exercise_file, "r", encoding='utf-8') as exercises, open(answer_file, "r",
                                                                           encoding='utf-8') as answers:
            for i, (exercise, answer) in enumerate(zip(exercises, answers), start=1):
                exercise = exercise.strip()
                answer = answer.strip()

                # 检查题目行是否以"题目X:"开头(X是题目编号)
                if exercise.startswith("题目"):
                    parts = exercise.split(":", 1)
                    if len(parts) == 2:
                        exercise = parts[1].strip()
                # 检查答案行是否以"答案X:"开头(X是题目编号)
                if answer.startswith("答案"):
                    parts = answer.split(":", 1)
                    if len(parts) == 2:
                        answer = parts[1].strip()

                try:
                    user_answer = eval(answer.replace('‘', '+'))
                    user_answer = Fraction(user_answer).limit_denominator()  # 将小数答案转换为分数表示
                    correct_answer = eval(add_parentheses(exercise).replace('÷', '/').replace('×', '*'))
                    correct_answer = Fraction(correct_answer).limit_denominator()  # 将小数答案转换为分数表示

                    if user_answer == correct_answer:
                        correct_indices.append(i)
                    else:
                        wrong_indices.append(i)
                except Exception as e:
                    print(f"在评分第 {i} 题时发生错误:{e}")

        return correct_indices, wrong_indices
    except FileNotFoundError:
        print(f"文件不存在")
        return FileNotFoundError

主要模块的流程图

image

性能分析

image

image

   在此项目中以生成题目功能作分析,其中参数设置为 -n 1000 -r 10,其中main.py运行时间为101ms,消耗时间最多的函数为generate_questions_and_answers(),调用一次花费90ms,占比89.1%,自身用时3ms,占比3%。generate_expression()调用1000次,花费53ms,占比53.5%。由于这两个函数在调用时也会调用其他函数,主要时间也花费在调用其他函数上,自身用时少。所以要改进代码就只能改进最常调用的基本函数,如generate_one_expression()generate_number()等,这些基本函数大多调用内置库运行,可以通过替换更可靠更高效的库,或者在循环条件设置上更加合理,从而减少不必要的循环,减少时间消耗。经过优化,可减少10%~20%的时间消耗。

单元测试与异常处理

  测试代码test_main.py中,共设置了8个单元测试和异常处理测试。

测试generate_number函数生成的数是否在合理范围内

    def test_generate_number(self):
        # 测试generate_number函数生成的数是否在合理范围内
        for _ in range(100):
            number = generate_number(10)
            self.assertTrue(0 <= number < 10)

测试generate_expression函数生成的表达式是否有重复

    def test_generate_expression_equivalence(self):
        # 测试generate_expression函数生成的表达式是否有重复
        range_limit = 10
        num_expressions = 1000
        generated_expressions = set()
        for _ in range(num_expressions):
            expression = generate_expression(range_limit)
            # 检查是否有重复的表达式,或者等效的表达式
            if expression in generated_expressions:
                assert False, f"Duplicate expression found: {expression}"
            # 将通过重复检测的表达式加入表达式集合中
            generated_expressions.add(expression)

测试convert_to_fraction函数是否正确将小数转换为分数

    def test_convert_to_fraction(self):
        # 测试convert_to_fraction函数是否正确将小数转换为分数
        decimal_values = [0.25, 0.5, 0.75, 1.2, 2.5]
        expected_fractions = [Fraction(1, 4), Fraction(1, 2), Fraction(3, 4), Fraction(6, 5), Fraction(5, 2)]
        for decimal, expected in zip(decimal_values, expected_fractions):
            result = convert_to_fraction(decimal)
            self.assertEqual(result, expected)

测试convert_fraction函数是否正确将假分数转换为带分数

    def test_convert_fraction(self):
        # 测试convert_fraction函数是否正确将假分数转换为带分数
        test_num1 = '5/3'  # 带分数为 1‘2/3
        test_num2 = '34/11'  # 带分数为 3’1/11
        test_num3 = '59/8'  # 带分数为 7‘3/8
        test_num4 = '2612/315'  # 带分数为 8‘92/315
        test_num5 = '383/40'  # 带分数为 9‘23/40
        converted_test_num1 = '1‘2/3'
        converted_test_num2 = '3’1/11'
        converted_test_num3 = '7‘3/8'
        converted_test_num4 = '8‘92/315'
        converted_test_num5 = '9‘23/40'
        if convert_fraction(test_num1) == converted_test_num1 and convert_fraction(test_num2) == converted_test_num2 and convert_fraction(test_num3) == converted_test_num3 and convert_fraction(test_num4) == converted_test_num4 and convert_fraction(test_num5) == converted_test_num5:
            self.assertTrue(1)
        else:
            self.assertFalse(0)

测试 add_parentheses 函数是否能正常为所有数加个括号

    def test_add_parentheses(self):
        # 测试 add_parentheses 函数是否能正常为所有数加个括号
        exp1 = "1 - 0 ÷ 4/5 - 3/7"  # 加括号后为 (1 )-( 0 )÷( 4/5 )-( 3/7)
        exp2 = "7 + 2 - 4 ÷ 3/7"  # 加括号后为 (7 )+( 2 )-( 4 )÷( 3/7)
        exp3 = "1/4 ÷ 2 - 6/7"  # 加括号后为 (1/4 )÷( 2 )-( 6/7)
        exp4 = "4/5 × 6 + 1/3"  # 加括号后为 (4/5 )×( 6 )+( 1/3)
        exp5 = "2 × 5/8"  # 加括号后为 (2 )×( 5/8)

        pare_exp1 = "(1 )-( 0 )÷( 4/5 )-( 3/7)"
        pare_exp2 = "(7 )+( 2 )-( 4 )÷( 3/7)"
        pare_exp3 = "(1/4 )÷( 2 )-( 6/7)"
        pare_exp4 = "(4/5 )×( 6 )+( 1/3)"
        pare_exp5 = "(2 )×( 5/8)"

        if add_parentheses(exp1) == pare_exp1 and add_parentheses(exp2) == pare_exp2 and add_parentheses(exp3) == pare_exp3 and add_parentheses(exp4) == pare_exp4 and add_parentheses(exp5) == pare_exp5:
            self.assertTrue(1)
        else:
            self.assertFalse(0)

测试grade_questions函数是否正确统计正确和错误的题目

    def test_grade_questions(self):
        # 测试grade_questions函数是否正确统计正确和错误的题目
        correct_indices, wrong_indices = grade_questions("test_exercises.txt", "test_answers.txt")
        self.assertEqual(len(correct_indices), 10)
        self.assertEqual(len(wrong_indices), 10)

异常处理-文件不存在的情况

    def test_invalid_file_paths(self):
        # 文件不存在的情况
        self.assertEqual(grade_questions('non_existent_file.txt', 'non_existent_file.txt'), FileNotFoundError)

异常处理-测试生成的表达式是否合法

    def test_generate_expression(self):
        # 测试generate_expression函数生成的表达式是否合法
        for _ in range(100):
            expression, expression_1 = generate_expression(10)
            if Fraction(eval(expression.replace('÷', '/').replace('×', '*'))) == ZeroDivisionError:
                self.assertFalse(0)
            else:
                self.assertTrue(1)

  如果所有测试都通过,则会输出以下信息。

Ran 8 tests in 0.029s
OK

PSP表格

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

项目小结

  我们的项目是实现一个自动生成小学四则运算题目的命令行程序,它可以根据用户的参数生成不同数量的题目,并且可以判断用户的答案是否正确。我们的项目主要分为四个模块,通过这四个主要模块可以比较好的完成题目的要求。

  • 生成随机数模块
  • 生成-表达式模块
  • 生成题目和文件模块
  • 统计题目对错模块

  我们认为我们的项目是成功的,因为我们实现了所有的需求,并且通过了测试用例。我们也遇到了一些困难和挑战,例如如何保证题目不重复,如何保证生成的表达式是合法的,如何处理分数的运算和格式化(假分数与带分数的转换,如何设计合理的函数等。我们通过查阅资料,讨论方案,调试代码等方式解决了这些问题。
  我们觉得结对编程是一种很好的学习方式,它可以让我们互相交流思想,互相帮助解决问题,互相监督进度,互相提高水平。我们在结对过程中也有很好的感受,例如相互信任,相互尊重,相互鼓励等,尤其在讨论时有更多更好的思路,而不仅局限于自己的想法。
  对彼此结对中的闪光点或建议分享:
钟同学在项目中表现得很出色,有很强的逻辑思维能力和行动能力,能够快速理解需求和设计方案,并且能够高效地实现代码。陈同学也有很好的沟通能力和团队合作精神,能够主动提出意见和建议,并且能够耐心地听取我的想法和反馈,是一个很好的结对伙伴。

标签:结对,题目,项目,生成,fraction,test,expression,generate
From: https://www.cnblogs.com/zhongxue56/p/17724819.html

相关文章

  • uniapp项目实践总结(二十二)分包优化和游客模式
    导语:这篇主要介绍应用分包和游客模式相关的内容。目录应用分包游客模式应用分包微信对于小程序的打包压缩后的代码体积是有限制的,网页和APP也可以适用分包功能,因此需要进行分包添加以及分包优化。分包添加在pages.json文件中添加分包的信息。例如:有一个名叫user的分......
  • 软件工程(结对编程项目)
    Github作业链接成员:刘鸿杰林程星软件工程计科21级4班作业要求作业3要求连接作业目标实现四则运算题目的命令行程序PSP表格PSP2.1ersonalSoftwareProcessStages预估耗时(分钟)实际耗时(分钟)Planning计划1010·Estimate·估计这个任务需......
  • 众多数据中台实施项目失败的原因?
    在当今数字化转型的浪潮中,数据中台作为关键的战略举措被越来越多的企业所关注和实施。然而,数据中台项目的实施过程中并不乏失败案例,这引业界对于数据中台失败原因的深入思考和分析。通过一些公开的信息和数据,可以学习和总线路一些数据中台失败的根本原因,从而避免类似的错误,实现真正......
  • idea设置项目启动的JVM运行内存大小
    idea设置项目启动的JVM运行内存大小场景在开发当中,idea默认服务启动要占用1G内存。其实每个项目本地开发和调试的时候,根本不需要1G内存,200M左右足以如果在微服务体系下,那效果更明显,相同的内存可以启动更多的服务刚好本人的电脑只有8G,公司的微服务项目启动后,电脑风扇疯狂的转动。解......
  • SpringBoot学习1(项目部署以及创建报错的解决)
    1.SpringBoot设计目的:简化Spring应用的初始搭建以及开发过程.2.空项目创建2.1查看更改自己的maven版本file-->settings有时候这里的mavenhomeusersettingsfilelocal..不是自己的maven文件夹,记得修改过来。 2.2创建modulefile-->projectstructure如果有一个module的......
  • 我的第一个C#项目
    这是大一用来学C/C++的vs2010,当时因为情怀没卸载(呜呜呜世界上最好的磊磊老师!)吃了一年灰没想到了大三还要为我服务() 今天试运行了一下C#项目,原本以为还需要安装一些什么插件,结果上手就能用(Java你看看人家!!!!) 点击文件——》新建——》项目 点击visualC#下了Windows(第一次创......
  • Go 项目的 MAKE 工具
    Go项目的MAKE工具MAKE工具是Linux和Unix系统中一种常见的自动化构建工具,通常用于管理和组织软件项目。在Go语言中,使用MAKE工具可以轻松地管理和构建项目,并自动执行诸如编译、测试、安装等复杂的操作。下面将介绍如何在Go项目中使用MAKE工具,并说明其主要优势......
  • java项目开发常用配置文件模板
    mybatisconfig文件1<?xmlversion="1.0"encoding="UTF-8"?>2<!DOCTYPEconfiguration3PUBLIC"-//mybatis.org//DTDConfig3.0//EN"4"http://mybatis.org/dtd/mybatis-3-config.dtd">5......
  • 【项目心得】在nest中使用fastify-cookie
    包安装确保你在nest项目中安装了 fastify, @fastify/cookie, @nestjs/platform-fastify 等包npmifastify@fastify/cookie@nestjs/platform-fastify fastify的引入和fastify-cookie的注册src/main.tsasyncfunctionbootstrap(){constlogger:Logger=new......
  • F5为OpenTelemtry项目提供降本增效的技术支持
    代码贡献将协议压缩率提高两倍,并降低大容量遥测及人工智能驱动项目的带宽成本西雅图 —2023年9月25日 — F5(NASDAQ:FFIV)今日宣布将继续支持云原生计算基金会OpenTelemetry项目。这是一个开源框架,旨在通过提供用于检测、生成、收集和导出遥测数据(指标、日志和跟踪信息)的......