项目——四则运算器

  Github:https://github.com/Hessess/SiZe

  首先,mark下阅读《构建之法》前三章喜欢的两句话:“我总觉得灵感是属于业余爱好者的。我们职业人工只是每天持续工作。今天你继续昨天的工作,明天你继续今天的工作,最终你会有所成就。”和“过早的优化是一切罪恶的根源。”

  不发表什么读后感,接下来进入正题——四则运算器

题目:

  (1)能自动生成小学四则运算题目,并且不能出现负数;

  (2)能支持真分数的四则运算;

思路:

  题目一看还挺简单,生成随机整数,然后进行四则运算,就是真分数这一点需要动点脑筋。然后就想做个界面出来,毕竟有个tkinter库,看了下书,嗯好像不难。脑子一闪又想把上学期实训时自学的一点Qt给用上,上网搜索“Qt python”,折腾折腾花去好多时间,什么都没做出来。还是先把四则运算做好吧。

  几个思路:

  (1)a-b,必须a>=b;

  (2)a÷b,避免出现a>b,并且a不能被b整除的情况,且b不为0;

  (3)题目要求的是支持真分数的四则运算,那么结果出现假分数是被允许的;

  (4)加载“tkinter”库,设计窗口来实现功能;

  (5)利用“random.randint(x,y)”函数来产生要运算整数,分数由两个随机整数生成,分母不为0;

  (6)加载“fractions”库,来实现分数的生成和四则运算;

  但是作业的要求不止于此,我觉得除编程外的的工作才是重点吧,像《构建之法》第二种讲的学习个人开发流程,代码测试,性能优化等。这些第一次接触,边走边看吧。

实现过程:

  关键函数:

  def button(): #按钮处理函数
  def Newq():#整数的加减乘除 
  def newF():#分数的加减乘除

  实现过程:

  首次按下按钮开始做题,生成题目。再次按下按钮,获取用户输出内容,与正确答案匹配,生成“题目+答案+用户答案+正确与否”字符串,放置Listbox控件;当做到30的倍数时,弹窗提示做题数目和正确数目。

代码说明:

  以下为关键的三个函数,思路和注释都在代码注明,这里不再赘述。

def button(): #按钮处理函数
    #buttonNew.place_forget() #隐藏button
    global Number
    global RAns
    global Rnum
    if Number is 0: #开始步骤
        EnterAns.delete('0','end')
        listAns.insert(0, "开始答题~~")
        buttonNew["text"] = "下一题"
        R = Newq()  # 获取新问题
        Q1["text"] = R[1]  # 问题
        RAns=R[0]
        Number+=1
    else:  #先判断上一题是否正确再生成新题目 每5道有一道为真分数的运算
        User_A = EnterAns.get()
        u=Q1["text"]+"="+str(RAns)+"    your "+User_A+":"
        if User_A == str(RAns): #匹配答案
            u += " right"
            Rnum+=1
        else:
            u += " wrong"
        listAns.insert(0, u)
        if Number%5!=0:
            R = Newq()  # 获取整数新问题
        else:
            R=newF() # 获取分数新问题
        Q1["text"] = R[1]  # 问题
        RAns = R[0]  #正确答案
        Number += 1
        EnterAns.delete('0', 'end') #清空输入框
    if Number%30 == 0 :
        sss='你做了'+str(Number)+'道题,对了'+str(Rnum)+'道'
        tkinter.messagebox.showinfo("well done",sss)

  

#整数的加减乘除
def Newq():
    s=['+','-','×','÷']
    q=[]
    s_num=random.randint(0, 3)
    if s_num is 0 :#加法
        a=random.randint(0,50)
        b=random.randint(0,50)
        q.append(a+b)
        q.append(str(a)+' '+s[s_num]+' '+str(b))
        return q
    elif s_num is 1 :#减法
        a=random.randint(0,50)
        b=random.randint(0,a)
        q.append(a - b)
        q.append(str(a) + ' '+s[s_num]+' ' + str(b))
        return q
    elif s_num is 2 :#乘法
        a=random.randint(0,20)
        b=random.randint(0,20)
        q.append(a * b)
        q.append(str(a) + ' '+s[s_num]+' ' + str(b))
        return q
    else : #除法
        a=random.randint(0,20)
        b=random.randint(1,20)
        if (a>b and a%b!=0): #避免出现 20/3 这样的问题
            tmp=a
            a=b
            b=tmp
        c=Fraction(a,b)
        q.append(str(c))
        q.append(str(a) + ' '+s[s_num]+' ' + str(b))
        return q

  

#分数的加减乘除  
def newF():
    s=['+','-','×','÷']
    q=[]
    s_num=random.randint(0, 3)
    t1 = random.randint(0, 20)
    if t1==0:
        t2=random.randint(1, 20)
    else:
        t2 = random.randint(t1, 20)
    a=Fraction(t1,t2)
    t1 = random.randint(1, 20)
    if t1==0:
        t2=random.randint(1, 20)
    else:
        t2 = random.randint(t1, 20)
    b = Fraction(t1, t2)
    if s_num is 0 :#加法
        q.append(a+b)
        q.append(str(a)+' '+s[s_num]+' '+str(b))
        return q
    elif s_num is 1 :#减法
        if a<b:
            tm=a
            a=b
            b=tm
        q.append(a - b)
        q.append(str(a) + ' '+s[s_num]+' ' + str(b))
        return q
    elif s_num is 2 :#乘法
        q.append(a * b)
        q.append(str(a) + ' '+s[s_num]+' ' + str(b))
        return q
    else : #除法
        c=Fraction(a,b)
        q.append(str(c))
        q.append(str(a) + ' '+s[s_num]+' ' + str(b))
        return q

  

测试运行:

单元测试:

  测试newF()函数,生成1000个式子,出现报错,错误信息在“fractions.py”中,报错信息行如下:

  

  经过查找,发现错误代码如下:

t1 = random.randint(0, 20)
t2 = random.randint(t1, 20)
a=Fraction(t1,t2)
t1 = random.randint(0, 20)
t2 = random.randint(t1, 20)
b = Fraction(t1, t2)

  错误原因一为生成分数的时候,当t1为0时,t2可能也为0,即分母为0;二为生成b时,未考虑其作为除数的情况,即它不能为0;经过修改,这部分代码为:

    t1 = random.randint(0, 20)
    if t1==0:
        t2=random.randint(1, 20)
    else:
        t2 = random.randint(t1, 20)
    a=Fraction(t1,t2)
    t1 = random.randint(1, 20)
    if t1==0:
        t2=random.randint(1, 20)
    else:
        t2 = random.randint(t1, 20)
    b = Fraction(t1, t2)

  问题解决。

  测试Newq()函数,生成1000个式子,无问题,如下:

    

全局测试:

      

  可以看到,没有负数出现,问题没有出现假分数,分数的四则运算和整数的除法结果也都是约分了而且正确的。

      

  再给个弹窗的效果图,为了不麻烦就不输答案直接点下一题下一题了,弹窗信息结果是30道对0道。

性能分析优化:

  PyCharm提供了性能分析工具Run——Profile,如下图所示。利用Profile工具可以对代码进行性能分析。

    

  性能统计界面由Name、Call Count、Time(ms)、Own Time(ms) 4列组成一个表格,见下图。表头Name显示被调用的模块或者函数;Call Count显示被调用的次数;Time(ms)显示运行时间和时间百分比,时间单位为毫秒(ms)。

     

   可以看到大部分的时间是在等待用户输入,内置函数占用时间最大的是按钮的事件处理函数“button()”,也是整个项目的核心,而这些时间也是被生成弹窗给占用了。下图为运行不到30次没有弹窗生成的结果,在依时间来排列第一页甚至看不到“button()”。

·    

  再看其他俩个主要函数:

  

  生成整数题目调用了25次,生成分数题目调用了5次,占用时间都为0。

  很遗憾,能力有限也找不到可以进行优化的地方。

PSP表格:

    预计耗时(分钟) 实际耗时(分钟)
Planning 计划 3  3
Estimate 估计这个任务需要多少时间 3  3
Development 开发 76  130
Analysis 需求分析  1  5
Design Spec 生成设计文档  0
Design Review 设计复审(和同事审核设计文档)  5  0
Coding Standerd 代码规范(为目前的开发制定合适的规范)  0
Design 具体设计 5  20
Coding 具体编码 40 90
Code Review 代码复审 5  10
Text 测试(自测,修改代码,提交修改)  20  5
Reporting 报告  12  22
Text Report 测试报告 8  15
Size Measurement 计算工作量  2  2
Postmortem & Process Improvement Plan 事后总结,并提出过程改进计划 2  5
Sum 合计 91  155

  比想象中的难得多,就这个分数的处理就想了挺久。设计文档和代码复审没做也不知道是什么,所以时间为0。至于测试,我是边写边测试边改,把所有时间放在了“具体编码”上,那测试的时间,就写最后一次测试,也就反复生成一千个式子来试试,最后发现个问题,然后修改的时间了。

  实际操作时间可能比表格写的久,因为花了一早上来完成代码写博客上传文件什么的,虽然中间有玩手机什么的。

  ---2018.4.17

原文地址:https://www.cnblogs.com/hesse/p/8859016.html