作业要求参见:https://edu.cnblogs.com/campus/nenu/2020Fall/homework/11245
结对伙伴:张文燕
说明:编程语言 python 编程工具 pycharm
1.项目分析及编程收获
1.1功能一:四则运算
支持出题4个数的四则运算题目,所有题目要求作者有能力正确回答 (提示:1/3 != 0.33333333333333333333333333333333,而是无限长)。
为了快出成果,你快速造个控制台的版本,包括以后改版成更优秀界面的核心功能,并考虑到扩展。
1.1.1功能一:重难点
重点:将操作数和运算符分别存放,按题目格式要求显示正确结果
难点:对无限小数的处理
1.1.2 重要代码展示
# 生成表达式 def expression(): op = ['+', '-', '*', '/'] num1 = random.randint(0, 10) num2 = random.randint(0, 10) num3 = random.randint(0, 10) num4 = random.randint(0, 10) op1 = random.choice(op) op2 = random.choice(op) op3 = random.choice(op) formula = str(num1) + str(op1) + str(num2) + str(op2) + str(num3) + str(op3) + str(num4) return formula def judeExpression(): i = 0 while(i != 1): try: a = expression() eval(a) i = i + 1 except: i = i return a # 将正确答案按要求形式输出 def answer(fraction): n1 = fraction.numerator n2 = fraction.denominator if len(str(float(fraction))) > 15: # 如果正确结果无限,则以带分数形式输出 sub = int(n1 / n2) n3 = n1 n1 = n1 % n2 if sub == 0: return '%d/%d' % (n1, n2) # 如果带分数的sub部分为0,则不输出这部分 if n3 < 0: return '%d %d/%d' % (sub, n2-n1, n2) else: return '%d %d/%d' % (sub, n1, n2) elif n2 == 1: # 结果是整数,原样输出 return n1 else: return float(fraction) # 结果是有限小数,原样输出 ret = [] # 存放表达式 for i in range(20): ret.append(judeExpression()) n = 0 for f1 in ret: print(f1+'=') print('?', end="") result = Fraction(eval(input())).limit_denominator() # 输入的值转分数 answer1 = Fraction(eval(f1)).limit_denominator() # eval()计算表达式的值并转分数 if result == answer1: n += 1 print('答对啦,你真是个天才!') else: print('再想想吧,答案似乎是{}喔!'.format(answer(answer1))) print('你一共答对%d道题,共20道题。' % n)
执行效果图:
1.1.3 功能一编程收获
通过对功能一的编程,我第一次接触到结对编程,两个人为了实现某一功能一起思考,讨论,让我有了很大的收获。实现四则运算并不是很难,但是要达到作业要求还是需要花费时间和精力的。刚开始我们只是写了一个简单的四则运算,并没有对小数和分数分别讨论。后来经过思考与讨论,我们使用了Python标准库fractions中的Fraction以及编写了dealAnswer函数处理,最终实现了题目的要求。
1.2功能二:支持括号
1.2.1 功能二重点、难点
重点:考虑括号成对出现以及匹配的情况
难点:匹配成对出现的括号
1.2.2 重要代码展示
def expression(): op = ['+', '-', '*', '/'] bra = ['(', '', ')'] # 0:生成左括号 1:不生成括号 2:生成右括号 num1 = random.randint(0, 10) num2 = random.randint(0, 10) num3 = random.randint(0, 10) num4 = random.randint(0, 10) left1 = random.randint(0, 1) left2 = random.randint(0, 1) left3 = random.randint(0, 1) right1 = random.randint(1, 2) right2 = random.randint(1, 2) right3 = random.randint(1, 2) op1 = random.choice(op) op2 = random.choice(op) op3 = random.choice(op) # 成对出现括号的情况5种 if left1 == 0: left2 = 1 left3 = 1 if right1 == 2: right2 = 1 right3 = 1 else: right2 = 2 right3 = 1 else: if left2 == 0: left3 = 1 right1 = 1 if right2 == 2: right3 = 1 else: right3 = 2 else: left3 = 0 right1 = 1 right2 = 1 right3 = 2 formula = bra[left1] + str(num1) + str(op1) + bra[left2] + str(num2) + bra[right1] + str(op2) + bra[left3] + str(num3) + bra[right2] + str(op3) + str(num4) + bra[right3] return formula
执行效果图:
1.2.3 功能二编程收获
在编程过程中我们出现的最大问题莫过于对于括号的生成的随机性判断,但是通过细心考虑,反复推敲,发现这个问题的实现难度并不大,随机的种类也不是无法操作的,最后通过查阅资料找到了枚举括号位置的可能性的方法,对随机功能成功实现
1.3功能三:限定题目数量,打印输出,避免重复
1.3.1 重点、难点:
重点:(1)命令行参数限定题目数量,在控制台判断参数中是否含有“-c”并控制生成的题目数量。
(2)对输出题目的个数的校验,即输入的个数必须为正整数,小数和负数不符合要求.
难点:判断表达式是否重复
1.3.2 重要代码展示
def writeFile(formula): with open("题目输出.txt", 'a+') as file: file.write(formula) file.write(' ') return print("文件写入失败!") def function12(): ret = [] # 存放表达式 for i in range(20): ret.append(judeExpression()) n = 0 # 表达式和答案写入文件 def function3(num): ret = [] if num.isdigit() and (int(num) > 0): for i in range(int(num)): ret.append(judeExpression()) n = 0 for f1 in ret: n = n + 1 if n == 1: if os.path.exists("题目输出.txt"): os.remove("题目输出.txt") answer1 = Fraction(eval(f1)).limit_denominator() # eval()计算表达式的值并转分数 formula = f1 + '=' + " " + str(answer(answer1)) # 将表达式、空格、答案连接 writeFile(formula) else: print("题目数量必须是 正整数。") def main(argv): if argv["c"] == "": function12() else: # print(argv["c"]) function3(argv["c"]) if __name__ == "__main__": ap = argparse.ArgumentParser() ap.add_argument("-c", "--c", default="", help="传入参数") argv = vars(ap.parse_args()) main(argv)
执行效果图:
通过命令行输入参数并显示表达式和答案截图:
通过命令行输入的题目数量不是正整数截图:
1.3.3 功能三编程收获
通过此次功能编程,我学习了如何校验题目个数。但是对于表达式重复问题,虽然我们还没有进行编码实现,但是有进行探讨、考虑解题思路。首先,在题目的解读上最开始有一些疑问,在作业要求中关于表达式重复问题给出了一个例子,1*2+3*2和2*(1+3)+0这两个式子是属于重复题,但是我和张文燕同学认为不是重复题,将猜想、疑问向老师、师兄请教之后,终于解开疑问,是因为面向的用户是“小学生”,所以加0和乘0认为是重复的。最终我们总结了一个思路大致如下:生成表达式1——转为逆波兰式——存入列表list1——(遍历列表并规范化)形成规范化的二叉树——将规范后的二叉树转为逆波兰形式字符串——存入列表list2,表达式2同理,但在存入列表list2之前先和此列表中的所有元素比较,如果列表list2已有,则舍弃此表达式,如果列表list2没有,则加入列表。
2.结对编程的体会
通过此次结对编程,在结对过程中真的有1+1>2的体会,我们各自发挥了各自的优势,会大大提升整体的时间和效率。在遇到问题时,两个人寻找解决方案的效率远远大于1个,张文燕编程能力远在我之上,所以本次作业很大程度上要归功于她,所以这里深深感谢她对于本次作业的付出。通过这次结对编程,我体会到了编程的魅力,对编程的热爱又增加了些许。
3.花费时间较长的事件,或收获较大的事件
第一:在功能2括号匹配问题中不断查资料,讨论。枚举了所有匹配的情况,花费了较长的时间。
第二:在功能3的表达式重复问题上花费时间比较多。从讨论、理解题目,到查阅资料、和同学交流之后有了一些思路,虽然还未实现编程,但收获很多,之后也会尝试编程实现,不断完善思路。
第三:在编码实现各个功能过程中,格式控制也花费了较多的时间。最开始不满足要求,然后会根据需求编写、修改代码,进行调试运行,再发现问题,查阅资料,解决问题,最后确定最终代码,满足格式要求。
第四:功能分析,在正式开始编程之前分析每个功能,想出特殊的测试用例以及解决方法。
第五:在三个功能完成之后,不断去寻找bug并解决,经过思考与讨论不断完善每个功能。