四则运算器——软件工程的第五次作业
Github项目地址: https://github.com/cliohl/calculator
PSP
PSP2.1 |
personal Software Process Stages |
预估时间(分钟) |
实际耗时(分钟) |
Planning |
计划 |
20 |
30 |
· Estimate |
· 估计这个任务需要多少时间 |
20 |
30 |
Development |
开发 |
190 |
268 |
· Analysis |
· 需求分析 (包括学习新技术) |
10 |
8 |
· Design Spec |
· 生成设计文档 |
/ |
/ |
· Design Review |
· 设计复审 (和同事审核设计文档) |
/ |
/ |
· Coding Standard |
· 代码规范 (为目前的开发制定合适的规范) |
/ |
/ |
· Design |
· 具体设计 |
60 |
90 |
· Coding |
· 具体编码 |
120 |
150 |
· Code Review |
· 代码复审 |
/ |
/ |
· Test |
· 测试(自我测试,修改代码,提交修改) |
10 |
20 |
Reporting |
报告 |
80 |
60 |
· Test Report |
· 测试报告+博客 |
60 |
50 |
· Size Measurement |
· 计算工作量 |
/ |
/ |
· Postmortem & Process Improvement Plan |
· 事后总结, 并提出过程改进计划 |
20 |
10 |
合计 |
|
300 |
358 |
题目描述:
(1)能自动生成小学四则运算题目,并且不能出现负数;
(2)能支持真分数的四则运算;
思路:
(1)四则运算加减乘除:由于基础不扎实,现只能实现两个随机正数的四则运算。
(2)由于不能出现负数,在进行减法运算的时候,我用了2种不同的方法实现,一种是先随机生成减数,然后在0到减数之间随机生成被减数,这样可以控制被减数一定小于减数,第二种是随机生成两个数,然后比较大小,大的数作为减数,小的数作为被减数。
(3)在进行除法运算时,因为被除数不能为零,所以在被除数的随机取值范围内不能包括0,在取随机分数的时候,分母不能为零,所以分母的随机取值范围也不包括0。
(4)在进行真分数运算的时候,导入python自带的fractions库。
(5)因为不包含假分数,所以在进行分数生成的时候也需要对分子和分母比较大小,大的数为分子,小的数为分母。
实现过程:
设计两个函数:
def create1(q,ans) 生成整数四则运算
def create2(q,ans) 生成真分数四则运算
def createf() 生成两个真分数
函数关系:
create1(q,ans)和create2(q,ans)均为独立的函数,负责生成四则运算。
主函数部分主要分为2大部分,第一个为测试部分,第二个为练习部分。测试部分可以自定义题目数量及测试类型,测试完毕给出测试的分数,并且可以选择是否查看答案。练习部分也可以自定义题目数量及测试类型,每完成一提都会判断结果对错,如果错误会给出正确答案。
代码说明:
详细代码如下所示:
1 def create1(q,ans)函数
1 def create1(q, ans): # 两个整数的四则运算 2 symbol = random.choice(['+', '-', '*', '/']) # 生成随机符号 3 if symbol == '+': 4 n1 = random.randint(0, 100) # 随机生成0到100的整数 5 n2 = random.randint(0, 100) 6 q.append(str(n1) + ' + ' + str(n2)+' = ') 7 ans.append(n1 + n2) 8 elif symbol == '-': 9 n1 = random.randint(0, 100) # 随机生成0到100的整数 10 n2 = random.randint(0, n1) 11 q.append(str(n1) + ' - ' + str(n2)+' = ') 12 ans.append(n1 - n2) 13 elif symbol == '*': 14 n1 = random.randint(0, 50) # 随机生成0到100的整数 15 n2 = random.randint(0, 50) 16 q.append(str(n1) + ' × ' + str(n2)+' = ') 17 ans.append(n1 * n2) 18 else: 19 n1 = random.randint(0, 50) # 随机生成0到100的整数 20 if n1 == 0: 21 n2 = random.randint(1, 50) 22 else: 23 n2 = random.randint(1, n1) 24 while n1 % n2 != 0: 25 n1 = random.randint(1, 50) 26 n2 = random.randint(1, n1) 27 q.append(str(n1) + ' ÷ ' + str(n2)+' = ') 28 ans.append(Fraction(n1, n2))
2 def createf()函数
1 def createf(): 2 # 生成2个分数 3 fz = random.randint(0, 20) 4 if fz == 0: 5 fm = random.randint(1, 20) 6 else: 7 fm = random.randint(fz,20) 8 f1 = Fraction(fz, fm) # 第一个分数 9 fz = random.randint(1, 20) 10 fm = random.randint(fz, 20) 11 f2 = Fraction(fz, fm) # 第二个分数 12 return f1, f2
3 def create2(q,ans)函数
1 def create2(q, ans): # 两个分数的四则运算 2 symbol = random.choice(['+', '-', '*', '/']) # 生成随机符号 3 f1, f2 = createf() 4 if symbol == '+': 5 while f1 + f2 > 1: 6 f1, f2 = createf() 7 q.append(str(f1) + ' + ' + str(f2)+' = ') 8 ans.append(f1 + f2) 9 elif symbol == '-': 10 f1, f2 = max(f1, f2), min(f1, f2) 11 q.append(str(f1) + ' - ' + str(f2)+' = ') 12 ans.append(f1 - f2) 13 elif symbol == '*': 14 while f1 * f2 > 1: 15 f1, f2 = createf() 16 q.append(str(f1) + ' × ' + str(f2)+' = ') 17 ans.append(f1 * f2) 18 else: 19 while Fraction(f1, f2) > 1: 20 f1, f2 = createf() 21 q.append(str(f1) + ' ÷ ' + str(f2)+' = ') 22 ans.append(Fraction(f1, f2))
4 主函数
1 while 1: 2 print("请输入类型:1、测试 2 、练习 3、退出") 3 t = int(input()) 4 if t == 3: 5 break 6 elif t == 1: # 测试 7 print("请输入你想要的四则运算:1、整数 2、分数 3、混合 4 、退出") 8 n = int(input()) 9 while n > 4: 10 print("选择类型有误!请重新选择") 11 n = int(input()) 12 if n == 4: 13 break 14 print("请输入你想要的题目数量(5的倍数):", end=' ') 15 qu = int(input()) 16 while qu % 5 != 0: 17 print("输入数目有误,请重新输入!") 18 qu = int(input()) 19 pscore = 100 / qu 20 sscore = 0 21 ques = [] 22 ans = [] 23 if n == 1: # 整数测试题目 24 25 for i in range(qu): 26 create1(ques, ans) 27 elif n == 2: #分数测试题目 28 for i in range(qu): 29 create2(ques, ans) 30 elif n == 3: # 混合测试题目 31 for i in range(qu): 32 n = random.randint(0, 3) 33 if n == 0: 34 create2(ques, ans) 35 else: 36 create1(ques, ans) 37 for i in range(qu): 38 print("第{}题:{}".format(i + 1, ques[i]),end=' ') 39 a = input() 40 if a == str(ans[i]): 41 sscore = sscore + pscore 42 print("你所得分数为:{}".format(sscore)) 43 print("是否查看正确答案? y/n", end=' ') 44 pan = input() 45 if pan == 'y': 46 for i in range(qu): 47 print(ques[i] + str(ans[i])) 48 elif t == 2: 49 print("请输入你想要的四则运算:1、整数 2、分数 3、混合 4、退出") 50 n = int(input()) 51 while n > 4: 52 print("选择类型有误!请重新选择") 53 n = int(input()) 54 if n == 4: 55 break 56 print("请输入你想要的题目数量:") 57 qu = int(input()) 58 ques = [] 59 ans = [] 60 if n == 1: # 整数练习题目 61 for i in range(qu): 62 create1(ques, ans) 63 elif n == 2: # 分数练习题目 64 for i in range(qu): 65 create2(ques, ans) 66 elif n == 3: # 混合练习题目 67 for i in range(qu): 68 n = random.randint(0,3) 69 if n == 0: 70 create2(ques, ans) 71 else: 72 create1(ques, ans) 73 else: 74 print("选择类型有误!请重新选择") 75 for i in range(qu): 76 print("第{}题:{}".format(i + 1, ques[i]),end=' ') 77 a = input() 78 if a == str(ans[i]): 79 print("回答正确!") 80 else: 81 print("回答错误!正确答案为:{}".format(ans[i])) 82 else: 83 print("选择类型有误!请重新选择")
测试运行:
单元测试:
测试create1(q,ans)函数,经过查找发现错误如下:
这里我忽略了当n1为0时的情况,导致运行出错。
改进如下:
问题解决。
测试create2(q,ans)函数,经过查找发现错误如下:
这里我没有考虑到当结果大于1成为假分数的情况,改进如下:
问题解决。
全体测试运行结果如下:
效能分析:
PyCharm提供了性能分析工具Run——Profile。
如下图所示:
性能统计界面由Name、Call Count、Time(ms)、Own Time(ms) 4列组成一个表格,见下图。
(表头Name显示被调用的模块或者函数;Call Count显示被调用的次数;Time(ms)显示运行时间和时间百分比,时间单位为毫秒(ms)。)
可以看到,用户输入占用了100%的时间,说明本代码的绝大部分时间都是在等待用户输入。
现在来看看create1(q,ans)函数,create2(q,ans)函数,create1()函数的性能测试。如下图所示:
可以看到,生成整数题目调用了20次,生成分数调用了8次,生成分数题目调用了5次,所占时间都为0,说明函数调用占比只占了很少的一部分。
本代码还有很大的改进空间,碍于能力与时间问题,就先不进行改进,日后有时间再修改代码及分析。
个人感想:
看到这个题目之后的第一感觉好像很简单,可是真正动手去做才知道有很多细节是需要注意的。刚开始我是想做一个可以自由控制多项式项数的,能够进行混合运算的四则运算器,可是后来发现有很多的小细节都卡住了思路,无奈之下只能先完成两项的混合运算,等日后有空再钻研混合运算及可以添加括号的四则运算器。通过这次这个小项目,可以发现自己许多的不足之处。原来做个项目是需要经过这么多的步骤才可以慢慢实现,通过阅读构建之法的前3章,清楚的认识到自己的水平,日后还有待加油。希望明天的自己会比今天的自己更好。