pyqt5 计算器可视化

实验任务(实验题目、目的)


Use VC++/Python to implement a software calculator with operational priorities.

利用 VC++/Python 语言实现+-*/运算,实现一个带运算优先级的计算器。

用栈实现计算器可视化的操作。

任务分析

1 将算式整理成列表formula_list。

 

2 循环[为方便描述,我们把此处循环叫循环1],依次取出列表中的元素 e (element缩写)。

     if  e 是数字:

        加入数字栈num_stack,获取下一个元素e。

         

     else e 不是数字(即是运算符):

         while True: (不断循环,此处是为了不断比较从算式列表中拿到的运算符和运算符栈中的最后一个运算符的优先级)

           如果运算符栈op_stack 为空:

           运算符 e 无条件加入运算符栈,并获取下一个元素e

             

           如果运算符栈不为空:

           取出运算符栈最后一个运算符 和 当前运算符e比较,得出一个决策。决策分为[入栈,出栈,0]三种。

           如果决策是入栈:

           将运算符加入运算符栈,并获取下一个元素e

           如果决策是出栈:

           弹出数字栈中的最后两位数字,运算符栈中的最后一个运算符。

         计算,把结果代替原来的数字。

          回退到while True循环

            如果决策是0,这种情况专门表示运算符栈的最后一个元素是"(" 而当前获取到的元素是 ")":

             弹出运算符栈中的最后一个运算符"(",并丢掉当前元素是")",获取下一个元素

             

3 上述处理完之后可能会存在一个问题:

当决策是0的时候,我们 弹出运算符栈中的最后一个运算符"(",并丢掉当前元素是")",获取下一个元素;

如果这个时候算式列表没有下一个元素了呢?此时既没有下一个元素,也没有继续运算,【循环1】结束了,但数字栈和运算符栈中可能还有元素。且运算符一定是同一级别的。

所以应该不断弹栈做运算,直到运算符栈中没有运算符为止。最后得到的结果就是数字栈的第一位元素。

实验设计(实验环境、算法、步骤、核心代码等)

逆波兰表达式

中缀表达式,例如1+3*2,中缀表达式的特点就是: 二元运算符总是置于与之相关的两个运算对象之间

后缀表达式,后缀表达式的特点就是:每一运算符都置于其运算对象之后,以上面的中缀表达式 1+2*3 为例子,转为后缀表达式就是 123*+

 

下面先分析怎么把 中缀表达式 转换为 后缀表达式, 这里我们考虑六种操作符 '+'、'-'、'*'、'/'、'('、')' ,完成中缀转后缀我们需要两个数组,都以栈的方式来操作,一个数组用来存放后缀表达式(char num[100]),

一个数组用来临时存放操作数(char opera[100])(这里说 临时 存放,是因为最后都要入栈到后缀表达式数组 num 中,这个数组就相当于一个中转站)

 

1、从左往右扫描中缀表达式(这里我们以1*(2+3)为例)

2、如果是数字那么将其直接入栈到数组 num

3、如果是操作数,需要进一步判断

(1)如果是左括号'('直接入栈到数组 opera

(2)如果是运算符('+'、'-'、'*'、'/'),先判断数组 opera 栈顶 的操作数的优先级(如果是空栈那么直接入栈到数组 opera ),如果是左括号那么直接入栈到数组 opera 中,如果栈顶是运算符,且栈顶运算符的优先级大于该运算符

那么将栈顶的运算符出栈,并入栈到数组 num 中,重复步骤3,如果栈顶运算符优先级小于该运算符,那么直接将该运算符入栈到opera中

(3)如果是右括号')',那么说明在 opera 数组中一定有一个左括号与之对应(在你没输错的情况下),那么将 opera 中的运算符依次出栈,并入栈到 num 中,直到遇到左括号'('(注意左括号不用入栈到 num

4、如果中缀表达式扫描完了,那么将 opera 中的操作数依次出栈,并入栈到 num 中就可以了,如果没有没有扫描完重复1-3步

上面就是中缀表达式转后缀表达式的步骤了,下面用图来直观的了解一下这个过程

 

需要注意的是:opera中操作数,越靠近栈顶,优先级越高,下面附上实现代码

 View Code

 

后缀表达式的计算

完成了中缀表达式转后缀表达式,接下来就是后缀表达式的计算了,后缀表达式的计算比中缀转后缀要稍微简单一点,只需要对我们转换好的后缀表达式从左往右依次扫描,并依次入栈就行了,

意思是只需要用一个数组(double num[100])就OK了

需要考虑的情况如下

1、如果是数字,那么直接入栈到num中

2、如果是运算符,将栈顶的两个数字出栈(因为我们考虑的运算符加、减、乘、除都是双目运算符,只需要两个操作数),出栈后对两个数字进行相应的运算,并将运算结果入栈

3、直到遇到'\0'

下面用几张图,来直观了解下这个过程,以上面转换好的后缀表达式"123+*"为例(这里用 ss 来存储后缀表达式, num 来存储计算结果,注意不要与上面图中 num 搞混淆了)

 

(注意:这里将计算结果5入栈后,栈顶从之前的[3]变成[2])

到这里后缀表达式的计算就结束了,下面附上实现代码

核心代码

def get_stack_value(input_str):

# 存放操作数

operator = []

# 存放操作符

operate = []

# 存放顶部操作符

top = ''

# 存放flag,flag为真,不添加当前字符

flag = False

# 临时操作数

temp_str = ''

# 预处理:basic_work(input_str)

input_str = tool.basic_work(input_str)

for index in range(len(input_str)):

# 遇到操作符,进行下面操作

if len(operate) >= 1:

top = operate[len(operate) - 1]

else:

top = ''

# 测试专用一:打印每次操作数和操作符

# print('top',operator)

# print('top',operate)

if input_str[index] == '+':

flag,temp_str = tool.set_operator_value(temp_str,operator)

tool.simple_caculate('+', top, 'out', input_str[index], operate, operator)

if input_str[index] == '-':

flag, temp_str = tool.set_operator_value(temp_str, operator)

tool.simple_caculate('-', top, 'out', input_str[index], operate, operator)

if input_str[index] == '*':

flag, temp_str = tool.set_operator_value(temp_str, operator)

tool.simple_caculate('*', top, 'out', input_str[index], operate, operator)

if input_str[index] == '/':

flag, temp_str = tool.set_operator_value(temp_str, operator)

tool.simple_caculate('/', top, 'out', input_str[index], operate, operator)

if input_str[index] == '%':

flag, temp_str = tool.set_operator_value(temp_str, operator)

tool.simple_caculate('%', top, 'out', input_str[index], operate, operator)

if input_str[index] == '^':

flag, temp_str = tool.set_operator_value(temp_str, operator)

tool.simple_caculate('^', top, 'out', input_str[index], operate, operator)

if input_str[index] == '&':

flag, temp_str = tool.set_operator_value(temp_str, operator)

tool.simple_caculate('&', top, 'out', input_str[index], operate, operator)

if input_str[index] == '.':

flag = False

if input_str[index] == '(':

operate.append(input_str[index])

flag = True

if input_str[index] == ')':

flag, temp_str = tool.set_operator_value(temp_str, operator)

if tool.compare_operation(')', top, 'out') == '<':

if len(operator) >= 2:

while operate[len(operate) - 1] == '(':

operate.pop()

operator.append(tool.compute_result(operator.pop(), operator.pop(), operate.pop()))

# 测试专用二:调试带括号的问题

# print(operator)

# print(operate)

if len(operate) >= 1 and operate[len(operate) - 1] == '(':

operate.pop()

else:

if len(operator) >= 2 :

operator.append(tool.compute_result(operator.pop(), operator.pop(), operate.pop()))

operate.pop()

# 遇到数字非法,直接退出;否则,组成临时字符

if (input_str[index] < '0' or input_str[index] > '9') and tool.not_operate(input_str[index]):

input_str = 'error in the\t'+str(index+1) + '\tcharacter!\t' + input_str + '->' + input_str[index]

return input_str

elif input_str[index] == ' ':

continue

else:

if flag:

flag = False

else:

temp_str += input_str[index]

# 遇到‘=’且为最后一个字符,返回结果

if input_str[index] == '=' and index == len(input_str)-1:

length = len(operate)

if length >= 1:

operate.pop()

else:

pass

while len(operate) > 0:

if len(operator) >= 2:

operator.append(tool.compute_result(operator.pop(), operator.pop(), operate.pop()))

else:

operate.pop()

return str(operator)

# 比较运算符优先级

def compare_operation(operate_one, operate_two, stack_type):

if operate_one == ')' and operate_two == '(':

return '<'

if operate_two == '' or operate_two == '(':

return '>'

if stack_type == 'out':

if '+' == operate_one:

if '(' == operate_two:

return '>'

else:

return '<'

if '-' == operate_one:

if '(' == operate_two:

return '>'

else:

return '<'

if '*' == operate_one:

if operate_two == '+' or operate_two == '-'or operate_two == '=':

return '>'

else:

return '<'

if '/' == operate_one:

if operate_two == '+' or operate_two == '-'or operate_two == '=':

return '>'

else:

return '<'

if '%' == operate_one:

if operate_two == '+' or operate_two == '-' or operate_two == '=':

return '>'

else:

return '<'

if '^' == operate_one:

if operate_two == '+' or operate_two == '-' or operate_two == '=' \

or operate_two == '*' or operate_two == '%' or operate_two == '/':

return '>'

else:

return '<'

if '&' == operate_one:

if operate_two == '+' or operate_two == '-' or operate_two == '=' \

or operate_two == '*' or operate_two == '%' or operate_two == '/':

return '>'

else:

return '<'

if ')' == operate_one:

if operate_two == '=':

return '>'

else:

return '<'

# 计算数值结果

def compute_result(number_one, number_two, operate):

if number_one == '':

number_one = '0'

if number_two == '':

number_two == '0'

param_one = float(number_one)

param_two = float(number_two)

if operate == '+':

return param_two + param_one

if operate == '-':

return param_two - param_one

if operate == '*':

return param_two * param_one

if operate == '/':

if param_one == 0:

return math.inf

return param_two / param_one

if operate == '%':

return param_two % param_one

if operate == '^':

if param_two < 0:

if param_one > 0 and param_one % 2 == 1:

return param_two ** (1/param_one)

elif param_one < 0 and param_one % 2 == 1:

return 1/(param_two ** (1/param_one))

else:

return math.inf

if param_two > 0:

if param_one > 0:

return param_two ** (1/param_one)

elif param_one < 0:

return 1/(param_two ** (1/param_one))

else:

return math.inf

if operate == '&':

return param_two ** param_one

实验结果

计算器的计算操作

表达式的处理会打印出来

计算的验证

评阅


本文章由word文档转换而成