20200924-5 四则运算试题生成,结对
作业要求参见:https://edu.cnblogs.com/campus/nenu/2020Fall/homework/11245
结对对象:蒋慧
说明:编程语言 python 编程工具 pycharm
要求1
(1) 给出每个功能的重点、难点、编程收获。
功能1. 四则运算
支持出题4个数的四则运算题目,所有题目要求作者有能力正确回答 (提示:1/3 != 0.33333333333333333333333333333333,而是无限长)。为了快出成果,你快速造个控制台的版本,包括以后改版成更优秀界面的核心功能,并考虑到扩展。
> f4
1+2*3+4=
?11
答对啦,你真是个天才!
1+2*3+5=
?11
再想想吧,答案似乎是12喔!
1+2/4-5=
?-3.5
答对啦,你真是个天才!
...(一共20道题)
你一共答对4道题,共20道题。
功能1重点:生成表达式、表达式计算、输入结果的判断及处理、按类型(整数、浮点数、带分数)显示正确结果形式、满足格式要求。
功能1难点:按类型(整数、浮点数、带分数)显示正确结果形式。
运行截图:
重要代码片段:
将每个表达式的输入结果与正确结果比较,并给出相应的提示信息。
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)
按要求形式输出正确答案的函数: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) # 结果是有限小数,原样输出
生成表达式函数:expression()
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
编程收获:
1.eval()其中一个功能是可以计算表达式的值。
2.Fraction(eval(f1)).limit_denominator() 将表达式f1的值转为分数。
3..format格式化输出字符串的函数(str.format),基本语法是通过{}和:代替以前的%。
4.fraction.numerator取分数的分子,fraction.denominator取分数的分母。
5.if len(str(float(fraction))) > 15: 表达式结果是无限小数,则用带分数形式输出正确结果。
6.需要通过不断测试去发现问题,解决问题。
7.两个人合作编程思路会比较多,解决问题也比较快。
功能2. 支持括号
老师看了你的表演,大大赞赏了你。然后她说,"你的题库里怎么都是没有括号的题呢,我记得你当初括号就掌握得不好啊。"你的脸红了,对老师说,"给我2个小时时间,我给你一个新版本,有括号的。"
你拿出笔记本,偷偷微信你们《构建之法》班的学霸,她说肯定能行,但是细节信号不好你听不清,只捕捉到隐约几个词"逆波兰""后缀表达式""堆栈""我看好你""数据结构"。
两小时后,在老师面前你在控制台下输入f4,然后回车。
> f4
1+2*(3+4)=
?15
答对啦,你真是个天才!
(1+2)*3+5=
?11
再想想吧,答案似乎是14喔!
((1/2)-4)*5=
?-17.5
答对啦,你真是个天才!
...(一共20道题)
你一共答对4道题,共20道题。
功能2重点:考虑括号可添加位置、如何表示是否生成括号、成对出现情况。
功能2难点:括号成对出现情况
运行截图:
重要代码片段:
括号生成:
bra = ['(', '', ')'] # 0:生成左括号 1:不生成括号 2:生成右括号 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) 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
编程收获:
1.用随机数字代表括号是否产生(1不产生,0/2产生)以及产生括号的位置(0产生左括号,2产生右括号),比较方便。
2.代码实现中没有用题目中提到的"逆波兰""后缀表达式""堆栈"解决括号匹配问题,而是用if else分情况讨论的。
功能3. 限定题目数量,"精美"打印输出,避免重复
"就这点儿题,像你当年做得那么快,一会儿就完成啦!"老师说,"另外,我想打印出来,上课也不能带台机器。又另外,你把答案也打出来呗,我把答案剪掉,题目给学生做。"
一看需求这么多,你生怕她会说,"这都是很简单的功能,你一定能完成吧"。你知道如果承诺今晚,明早交工的时候她一定想出了更多可怕的需求,你赶紧说,"老师我现在就做。"
你忘记怎么调用打印机了,就把答案与题目横向对齐,输出在文件的右边。告诉老师txt文件可以用WORD打开,也能打印。她满意而意味深长地笑了,表示同意。
你输入命令执行的时候,脑袋比手指头还疼。
>f4 -c 3
1+2*(3+4)= 15
(1+2)*3+5= 14
((1/2)-4)*5= 17.5
你指着屏幕对老师说,">"是提示符,实际运行时可能是"C:\Users\Young>","f4"是程序的名字,它真正的名字是"f4.exe","-c 3"是命令行参数。按下回车,此时程序开始执行。"-c"这个参数后面输入多少,就生成多少题目。老师输入
>f4 -c -200
题目数量必须是 正整数。
>f4 -c 3.5
题目数量必须是 正整数。
>f4 -c test
题目数量必须是 正整数。
老师欣慰的笑了。你怔怔地看着她,心下怀疑她真的是教初中数学的那位么。
老师在你的指导下试用了一下,打印出来的题目堪称精美。老师让你做一次试试看能得多少分。你不敢借口四则运算忘光了,只好提起笑来开始答。刚做一半,你发现有几道题目是相同的,心中暗骂随机数不靠谱。又有几道虽然不完全相同,但是可以通过交换律、结合律、分配律变换为同一道题。比如
1*2+3*2
2*(1+3)+0
功能3重点:如何通过命令行输入参数(-c和限定表达式个数num)、判断输入参数num是否正整数、表达式结果输出格式控制、判断表达式是否重复、将表达式及答案写入文件。
功能3难点:判断表达式是否重复
运行截图:
命令行输入的题量不是正整数截图:
通过命令行输入参数并将表达式和答案写入文件截图:
重要代码片段:
通过命令行输参数:
def main(argv): if argv["c"] == "": function12() else: function3(argv["c"]) if __name__ == "__main__": ap = argparse.ArgumentParser() ap.add_argument("-c", "--c", default="", help="传入参数") argv = vars(ap.parse_args()) main(argv)
将限定表达式和答案写入文件:
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 + '=' + "\t\t" + str(answer(answer1)) # 将表达式、空格、答案连接 writeFile(formula)
def writeFile(formula): with open("题目输出.txt", 'a+') as file: file.write(formula) file.write('\n') return print("文件写入失败!")
编程收获:
1.通过命令行传参数可以用Python 提供的getopt 模块,也可以用sys 的 sys.argv。最后我们使用的是argparse。
2.对传入的参数判断是否只由数字组成,采用str.isdigit()判断。
3.将表达式和答案写入文件:题目输出.txt,每次执行命令生成第一个表达式后如果文件存在,则移除,新建,写入。
4.关于表达式重复问题,虽然我们还没有进行编码实现,但是有进行探讨、考虑解题思路。首先,在题目的解读上最开始有一些疑问,在作业要求中关于表达式重复问题给出了一个例子,1*2+3*2和2*(1+3)+0这两个式子是属于重复题,但是我和蒋慧同学感觉不是重复题,因为要求中提到能将表达式通过交换律、结合律、分配律转变为同一题的是重复题。接着我们查阅了加法、乘法交换律及结合律,乘法分配律,发现第二个式子有+0还涉及到计算问题,后来猜想可能是因为题目是要求四个数的四则运算,在保证结果一致的情况下才加0的,只有0比较特殊。将猜想、疑问向老师、师兄请教之后,终于解开疑问,是因为面向的用户是“小学生”,所以加0和乘0认为是重复的。其次,在思路上,通过查阅资料,总结了一个思路大致如下:生成表达式1——转为逆波兰式——存入列表list1——(遍历列表并规范化)形成规范化的二叉树——将规范后的二叉树转为逆波兰形式字符串——存入列表list2,表达式2同理,但在存入列表list2之前先和此列表中的所有元素比较,如果列表list2已有,则舍弃此表达式,如果列表list2没有,则加入列表。除了这种方法,也可以用表达式——二叉树——如果不平衡转为平衡二叉树——规范二叉树——看规范化后的二叉树是否一致这种方法,以上两种方法都可以解决加法、乘法交换律的情况,剩下就是乘法分配律情况,如1*2+3*2和2*(1+3)+0,加0或乘0情况,这种则需要重新考虑新的方法,比如:2*(1+3)+0可以提前判断将+0此项去掉,然后展开再和1*2+3*2比较。有待进一步改进和实现。
(2)结对编程的体会
通过和蒋慧同学结对编程,认识到了结对编程有很多好处,比如两个人结对编程会产生更多的思路,不会局限在某一方面。遇到问题时,两个人可以一起讨论,一起解决,在编程的过程中也充分认识到了合作的重要性。总的来说,结对编程相比两个人独立编程会更节省时间,效率更高。另外,通过结对编程我和蒋慧同学相比之前也更加默契。
(3)至少5项在编码、争论、复审等活动中花费时间较长,给你较大收获的事件。(10分)
第一,在功能3的表达式重复问题上花费时间比较多。从讨论、理解题目,到查阅资料、和同学交流之后有了一些思路,虽然还未实现编程,但收获很多,之后也会尝试编程实现,不断完善思路。
第二,在编码实现各个功能过程中,格式控制也花费了较多的时间。最开始不满足要求,然后会根据需求编写、修改代码,进行调试运行,再发现问题,查阅资料,解决问题,最后确定最终代码,满足格式要求。
第三,在初次编写完3个功能后,有对代码进行复审,这一项花费时间比较多。初次编写各个功能的过程中,存在一些未解决的问题或者不符合功能要求的问题。复审代码是非常必要的,复审代码主要是为了完善每个功能代码并整理、确定每个功能的最终代码,另外还要尽可能的加上详细的代码注释,方便读者理解代码。在复审的过程中还可能会发现一些bug、漏洞等 ,可以有效减少代码错误、缩进等格式问题,从而更满足功能要求。
第四,编码过程中查阅Python基本语法这部分也占用了不少时间。由于对Python语言的不熟悉,尤其是Python中的一些函数、方法的使用,导致需要花费一定时间去查阅并应用。一方面可以对自己已掌握的函数、方法有更深、更全的理解,另一方面,完善自己的函数库。在这个过程中,发现自己对于一些函数出现了记忆偏差问题,导致给编码实现带来了一定的困难。所以对已经用过、学过的语法要不断去巩固,加深记忆。
第五,在编码过程中,通过命令行传参数在前两周的作业中已经涉及过,但是在编写功能3的过程中,还是不能很快地编写实现,逻辑上认为没问题,实践中会出现意向不到的问题。认识到代码放到具体问题中,还是需要进行调整,甚至是细微的,通过不断调试去解决问题。
要求2 给出结对开发的截图证据,要求截图能够证明你们是在结对编程。 (5分)
要求3 使用coding.net做版本控制。checkin 前要求清理 临时文件、可执行程序,通常执行 build-clean可以达到效果。(25分)
coding.net代码地址(需pull):https://e.coding.net/zhangwenyan1/arithmetic0/arithmetic.git