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

posted @ 2020-10-05 16:43  zwh12  阅读(202)  评论(2编辑  收藏  举报