zoukankan      html  css  js  c++  java
  • 软工第五次作业——Python效能分析之四则运算生成器

    Github项目地址:

    https://github.com/JtvDeemo/elementary-arithmetic

    PSP

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

    题目要求:

    • 能自动生成小学四则运算题目
    • 除了整数外,还要支持真分数的四则运算
      除了以上的基本需求,还有
    • 生成的算式长度随机
    • 能处理分数的运算,结果能用分数(字符串类型)表示出来,能与用户输入相对应

    解题思路:

    • 定义一个函数用于随机生成随机长度的算式
    • 把字符串型的算式转换为逆波兰式(RPN,也称后缀表达式)
    • 再把后缀表达式利用栈结构计算出结果
    • 最后再与用户的输入做比较

    重点难点:

    1. 分数的表示与计算
    2. 后缀表达式的生成和计算
    3. 结果为负数的情况

    如何解决:

    1. Python的分数计算可以用 fractions 库
    2. https://blog.csdn.net/qq_36763635/article/details/72627601 这里介绍了如何将中缀表达式转换为后缀表达式(RPN)
    3. https://blog.csdn.net/yangquanhui1991/article/details/52187375 图解后缀表达式的计算过程
    4. 对于结果为负数的情况,只能生成算式之后验算一遍,若为负数的情况,重新生成一遍(可能是个人水平有限)

    设计实现:

    具体程序设计:

    成员变量

    成员名 类型 功能
    op list 存放运算符
    quest str 存放算式
    lens int 2到9的随机长度
    teop str 存放当前运算符
    tstr str 存放当前算式
    tint int 存放当前运算数

    成员函数

    函数名 输入 输出 依赖函数 功能
    get_string 字符串 随机生成一个算式
    get_ans str 返回布尔类型 get_string 将用户输入与正确答案比较
    to_rpn str 返回后缀表达式 get_ans 将随机生成的算式转换为RPN
    this_bigger str,str 返回布尔表达式 冇啊 比较两个运算符的优先级
    slove_rpn str 返回计算结果 get_ans 将后缀表达式计算出来

    核心代码:

    #随机生成一个算式
        def get_string(self):
            self.lens = random.randint(2, 9)
            self.teop = ''
            self.tstr = []
            for i in range(self.lens):
                if self.teop == '÷':
                    self.tint = random.randint(1, 8)
                    self.teop = random.choice(self.op)
                elif self.teop == '/':
                    self.tint = random.randint(self.tint+1, 9)
                    self.teop = random.choice(self.op[:-1])
                else:
                    self.tint = random.randint(0, 8)
                    self.teop = random.choice(self.op)
                self.tstr.append(str(self.tint))
                self.tstr.append(self.teop)
            self.tstr[-1] = '='
            self.tstr = ''.join(self.tstr)
            self.quest = self.tstr
            return self.tstr
    
    #将随机生成的算式转换为RPN
        def to_rpn(self, ques):  #Reverse Polish notation
            self.stack = []
            s = ''
            for x in ques:
                if x != '+' and x != '-' and x != '×' and x != '÷' and x != '/':
                    s += x  #若为数字,直接输出
                else:  # 若为运算符,进栈
                    if not self.stack:  #栈空
                        self.stack.append(x)
                    else:
                        if self.this_bigger(x, self.stack[-1]):  #运算级高于栈顶元素
                            self.stack.append(x)  #直接进栈
                        else:
                            while self.stack:
                                if self.this_bigger(x, self.stack[-1]):
                                    break
                                s += self.stack.pop()
                            self.stack.append(x)
            while self.stack:
                s += self.stack.pop()
            # print('在to_rpn函数中,rpn:',s)
            return s
    
    #将后缀表达式计算出来
        def slove_rpn(self, rpn):
            #print('进入slove_rpn函数:')
            self.stack1 = []  #用于保存运算数
            for x in rpn:
                if x != '+' and x != '-' and x != '×' and x != '÷' and x != '/':
                    self.stack1.append(int(x))
                elif x == '+':
                    second = self.stack1.pop()
                    first = self.stack1.pop()
                    self.stack1.append(first + second)
                elif x == '-':
                    second = self.stack1.pop()
                    first = self.stack1.pop()
                    self.stack1.append(first - second)
                elif x == '×':
                    second = self.stack1.pop()
                    first = self.stack1.pop()
                    self.stack1.append(first * second)
                elif x == '÷':
                    second = self.stack1.pop()
                    first = self.stack1.pop()
                    self.stack1.append(Fraction(first, second))
                elif x == '/':
                    second = self.stack1.pop()
                    first = self.stack1.pop()
                    self.stack1.append(Fraction(first, second))
            resault = self.stack1[0]
            if resault >= 0:
                #print('--------------题结束----------------')
                return resault
            elif resault < 0:
                s = self.get_string()
                rpn = self.to_rpn(s[:-1])
                return self.slove_rpn(rpn)
    

    运行效果:

    单元测试

    《构建之法》第二章中详细提及了好的单元测试的标准。

    • 单元测试应该在最基本的功能/参数上检验程序的正确性。
    • 单元测试必须由最熟悉代码的人来写。
    • 单元测试过后,机器状态保持不变。
    • 单元测试要快。
    • 单元测试应该产生可重复、一致的结果。
    • 独立性-单元测试的运行/通过/失败不依赖于别的测试,可以人为构造数据,以保持测试的独立性。
    • 单元测试应该覆盖所有代码路径。

    效能分析图

    Pycharm中运行profiler,次数为10W次

    可以看到所用的时间长度为16.04s

  • 相关阅读:
    跨浏览器的事件处理程序
    jQuery提交Form
    js 多种变量定义(对象直接量,数组直接量和函数直接量)
    Asp.net Eval 截取字符串
    sql执行顺序
    IDEA File Templates模板
    Vim快捷键大全(转)
    Rider+Unity+XLua环境配置
    一定要进行代码复查
    如何在ubuntu上面安装luasocket
  • 原文地址:https://www.cnblogs.com/deemoo/p/8877981.html
Copyright © 2011-2022 走看看