20162116 实验二《Python程序设计》实验报告

20162116 2019-2020-2 《Python程序设计》实验二报告

课程:《Python程序设计》
班级: 1621
姓名: 韩浩
学号:20162116
实验教师:王志强
实验日期:2020年4月11日
必修/选修: 公选课

1.实验内容

  • (1)设计并完成一个完整的应用程序,完成加减乘除模等运算,功能多多益善。
  • (2)考核基本语法、判定语句、循环语句、逻辑运算等知识点。

2. 实验过程及结果

(1)基本思想

该实验要制作一个计算器,其中包含加减乘除、求余、乘方等运算。除此之外,用户要在pycharm界面进行输入,所以我们要对用户输入的字符串进行识别完成相应的计算过程,但是普通的中项式计算过于繁琐,需要走一步看好几步,太过麻烦,为此要采用转换为左项式的相应办法,利用学到的双栈算法来解决此问题。

(2)算法逻辑

双栈算法对应逻辑其实也很简单,俗话来说就是“边存边看,边走边算”,设置两个栈,一个为符号栈另一个为数字栈,符号栈中存储运算符,数字栈中存储数字,将指针从算术表达式的最左端向右移,如果是数字就进数字栈,如果是符号呢就进入符号栈,不过符号进栈前要下看看栈顶元素是什么,比较优先级大小,如果该符号的优先级大就直接进栈,否则就将栈顶元素弹出与数字栈最上面两个数据进行计算,再压入数字栈中,以此过程进行迭代,直到符号栈中元素为空。

(3)主要代码介绍

  • 计算函数:
    计算函数其实就是否则数字栈弹栈后而进行的计算操作,对应只要识别好运算符,就没有问题。
def calculate(n1, n2, operator):
    result = 0
    if operator == "+":
        result = n1 + n2
    if operator == "-":
        result = n1 - n2
    if operator == "*":
        result = n1 * n2
    if operator == "/":
        result = n1 / n2
    if operator == "%":
        result = n1 % n2
    if operator == "^":
        result = n1 ** n2
    return result
  • 判断运算符函数:
    该函数其实也比较简单,提前设置好运算符集合的序列,然后判断是否在里面,直接返回bool类型返回值即可。
def is_operator(e):
    opers = ['+', '-', '*', '/', '%', '(', ')', '^']
    return True if e in opers else False
  • 识别输入字符函数:
    该函数相对来说就要稍微花点心思了,但其实只要re库用的好,也不必担心,该库中给我们提供了相应的字符串检索、分离的函数,这样的情况下,我们就可以使用正则表达式,来进行数据的分离,将其存储在新的序列当中,最后所呈现的就是数字、符号是分割开的每个元素,这样我们的效果就达到了。
def formula_format(formula):
    formula = re.sub(' ', '', formula)
    formula_list = [i for i in re.split('(\-\d+\.?\d*)', formula) if i]
    print(formula_list)
    final_formula = []
    for item in formula_list:
        if len(final_formula) == 0 and re.search('^\-\d+\.?\d*$', item):
            final_formula.append(item)
            continue
        if len(final_formula) > 0:
            if re.search('[\+\-\*\/\(]$', final_formula[-1]):
                final_formula.append(item)
                continue
        item_split = [i for i in re.split('([\+\-\*\/\(\)\%\^])', item) if i]
        final_formula += item_split
    return final_formula
  • 优先级决策函数:
    该函数的主要功能就像我在上文中提到的那样,判断符号是否要进入符号栈,根据提前设置好的优先级表,使用if-elif-else语句来输出对应接下来需要执行的操作。
def decision(tail_op, now_op):
    rate1 = ['+', '-']
    rate2 = ['*', '/', '%']
    rate3 = ['^']
    rate4 = ['(']
    rate5 = [')']
    if tail_op in rate1:
        if now_op in rate2 or now_op in rate3 or now_op in rate4:
            return -1
        else:
            return 1

    elif tail_op in rate2:
        if now_op in rate3 or now_op in rate4:
            return -1
        else:
            return 1

    elif tail_op in rate3:
        if now_op in rate4:
            return -1
        else:
            return 1

    elif tail_op in rate4:
        if now_op in rate5:
            return 0
        else:
            return -1
    else:
        return -1
  • 过程函数:
    该函数其实就是个流程函数,利用迭代思想控制整个代码的走向和运行,因为上述程序已经完成了各个模块的关键功能,首先将字符串识别分割开来,然后对每个元素进行判断,数字就直接进数字栈,符号的话就用优先级判决函数输出相应指令,-1压栈,0弹栈,1就弹出两个数字和一个符号进入计算函数中计算,然后再压栈进入迭代过程,不过我们要注意的是,等迭代结束后我们仍要判断符号栈是否存在未弹栈的元素,如果有就继续配合数字栈进行计算,直到栈空。
def final_calc(formula_list):
    num_stack = []
    op_stack = []
    for e in formula_list:
        operator = is_operator(e)
        if not operator:
            num_stack.append(float(e))
        else:
            while True:
                if len(op_stack) == 0:
                    op_stack.append(e)
                    break
                tag = decision(op_stack[-1], e)
                if tag == -1:
                    op_stack.append(e)
                    break
                elif tag == 0:
                    op_stack.pop()
                    break
                elif tag == 1:
                    op = op_stack.pop()
                    num2 = num_stack.pop()
                    num1 = num_stack.pop()
                    num_stack.append(calculate(num1, num2, op))
    while len(op_stack) != 0:
        op = op_stack.pop()
        num2 = num_stack.pop()
        num1 = num_stack.pop()
        num_stack.append(calculate(num1, num2, op))
    return num_stack

(4)实验结果


由该图可知,该代码成功实现加减乘除、求余、乘方等计算功能,其中还包含括号匹配等相关知识点。

(5)码云链接

https://gitee.com/hanhao16/hanhao17/blob/master/test8.py

3. 实验过程中遇到的问题和解决过程

  • 问题1:怎么正确识别负号和运算符之间的差异,该如何判断?
  • 问题1解决方案:通过上网查资料,了解到有re库,可以使用相关函数进行元素的分离,并且看到了别人的负数判断方法,若是第一个元素就为符号-则为负数,另外如果该序列最后的元素已经为某符号的话也可以判断该数据为负数。
  • 问题2:如何使用正则表达式将'*'和'**'与数据能够成功分离?
  • 问题2解决方案:其实很简单,因为我们日常生活中**并不是乘方的运算符号,我们只要将识别内容改变为^即可,这样程序也不会产生混淆。
  • ...

4.感悟体会

  • (1)就我个人而言,这道题其实可能不算难题,但对于没有学习过数据结构的同学们的话,不知道他们会采用什么样的方法来实现计算器的各种混合运算,其实这个实验给我比较直观的体会是算法的优越性,因为编程发展至今,前人总结了不少简便、高效的算法供我们使用,而在日后学习当中,注意算法的积累会帮助我们编程效率的提高和能力的增强。
  • (2)第二点比较直观的感受就是,python不愧为年轻的编程语言,它的的设计中考虑了许许多多方便用户的细节,就比如说re库、正则表达式等,这些技巧的使用可以大大减少代码量,并且提高编程效率,但这其实有个前提,就是得知道有哪些库、哪些功能的存在,如果不知道的话,使用自然是无稽之谈,所以也提醒我们要对于python的一些库函数多使用、多实践,在积累中增长学识;在点滴间扩充认知,这样才能提高进步。

参考资料

  • 《简明Python教程》

  • 《python标准库》

  • ...

posted @ 2020-04-11 17:37  韩浩  阅读(628)  评论(0编辑  收藏  举报