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 | 0 |
Design Review | 设计复审(和同事审核设计文档) | 5 | 0 |
Coding Standerd | 代码规范(为目前的开发制定合适的规范) | 0 | 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