Skip to content

M02694: 波兰表达式

recursion, strings, http://cs101.openjudge.cn/pctbook/M02694/

波兰表达式是一种把运算符前置的算术表达式,例如普通的表达式2 + 3的波兰表示法为+ 2 3。波兰表达式的优点是运算符之间不必有优先级关系,也不必用括号改变运算次序,例如(2 + 3) * 4的波兰表示法为* + 2 3 4。本题求解波兰表达式的值,其中运算符包括+ - * /四个。

输入

输入为一行,其中运算符和运算数之间都用空格分隔,运算数是浮点数。

输出

输出为一行,表达式的值。 可直接用printf("%f\n", v)输出表达式的值v。

样例输入

* + 11.0 12.0 + 24.0 35.0

样例输出

1357.000000

提示

可使用atof(str)把字符串转换为一个double类型的浮点数。atof定义在math.h中。 此题可使用函数递归调用的方法求解。

来源:计算概论05

2021fall-cs101,王顾言。

这道题理解题意花了不少时间,对题目下面给出的使用递归函数求解更是迷惑。 个人觉得和 OJ.03704括号匹配 问题非常相似,手动建了个栈,一通操作后ac。 做完后去看题解,画了个流程图才理解 递归解法 绝妙之处。

image-20211220025155202

2021fall-cs101,欧阳韵妍。

解题思路:关键:碰到两个数字就按照离两个数字左边最近的运算符运算,注意由于输入的是 str,需要手动定义输入的运算符对应的运算操作。注意 operate 是一层层套下去的,比如:* + 11.0 12.0 + 24.0 35.0 就是这样的运算步骤:

()*()→

(()+())*()→

((11.0)+(12.0 ))* ()→

((11.0 )+ (12.0 ))* (()+ ())→

((11.0 )+ (12.0 ))*((24.0)+(35.0))

一个运算符相当于进行一次递归。学会了输出精确到两位小数点的方法,用”{:.2f}”.format()。

python
'''
前缀表达式是运算符在前,操作数其后,
就是假如碰到一个运算符,其后就需要有连续的两个操作数才能运算消去,
否则就一直等待输入或者等待后面的运算结束得到操作数,
这恰好能用递归实现。
'''
# pylint: skip-file
def Exp():
    global pos
    pos += 1
    chr = calculatelist[pos]
    if chr == '+':
        return Exp() + Exp()
    elif chr == '-':
        return Exp() - Exp()
    elif chr == '*':
        return Exp() * Exp()
    elif chr == '/':
        return Exp() / Exp()
    else:
        return float(chr)



calculatelist = []
pos = -1
calculatelist = list(input().split())
result = Exp()
print(f"{result:.6f}")

2021fall-cs101,李文梁。

由于python 可以直接计算字符串表达式,只需要把波兰表达式转换成正常计算式即可。具体操作为递归,嵌套到从最内层开始把符号换到数字之间并计算,一层层返回即可。

python
s = input().split()
def cal():
    cur = s.pop(0)
    if cur in "+-*/":
        return str(eval(cal() + cur + cal()))
    else:
        return cur

print(f"{float(cal()):.6f}")

2021fall-cs101,黄湘榆。

这题像是一道语法题。我利用了之前做24 点时学到的operator 标准库来定义一个运算符的字典,方便随时根据需求切换运算符。

import operator 
op = {"+": operator.add, "-": operator.sub, "*": operator.mul, "/": operator.truediv}
python
import operator 
s = input().split() 
op = {"+": operator.add, "-": operator.sub, "*": operator.mul, 
"/": operator.truediv} 
while len(s) != 1: 
    for i in range(len(s)): 
        if s[i][0].isdecimal() and s[i+1][0].isdecimal(): 
            o = s.pop(i-1) 
            a = s.pop(i-1) 
            b = s.pop(i-1) 
            c = op[o](float(a), float(b)) 
            s.insert(i-1, str(c)) 
            break 
 
print(f"{float(s[0]):.6f}")

2021fall-cs101,陈锦洋。

不得不说eval 函数在这里帮了大忙。递归在这里是很好的想法。

python
l=input().split()
def poland():
    op=l.pop(0)
    a=poland() if l[0] in '+-*/' else l.pop(0)
    b=poland() if l[0] in '+-*/' else l.pop(0)
    return eval(str(a)+op+str(b))
print(f'{poland():.6f}')

2021fall-cs101,陈思同。

break用的挺巧妙,悬崖勒马的感觉。明明边循环边删除,就要出错了感觉。

python
b = ['+', '-', '*', '/']
a = input().split()
for i in range(len(a)):
    if a[i] not in b: a[i] = float(a[i])
while 1:
    if len(a) == 1:
        break
    for i in range(len(a)):
        if a[i] in b and a[i+1] not in b and a[i+2] not in b:
            if a[i] == '+': d = a[i+1] + a[i+2]
            elif a[i] == '-': d = a[i+1] - a[i+2]
            elif a[i] == '*': d = a[i+1] * a[i+2]
            elif a[i] == '/': d = a[i+1] / a[i+2]
            
            a[i] = d
            del a[i+1] 
            del a[i+1]
            break
print(f'{a[0]:.6f}')

2021fall-cs101,渠成锐。

思路:最一开始接触到函数递归调用就是这道题,当时惊赞题解里的做法真的是妙哉。现在学了栈这种数据结构,我发现可以写一个不用函数递归调用的版本。基本想法如下:从后往前读取表达式,碰见数字则压入栈,碰见运算符号则从栈顶弹出两个数据进行计算,将计算结果再压入栈即可。

python
expression = input().split()
stack = []
while expression:
    a = expression.pop(-1)
    if a in ['+', '-', '*', '/']:
        c = stack.pop(-1)
        d = stack.pop(-1)
        if a == '+':
            stack.append(c + d)
        elif a == '-':
            stack.append(c - d)
        elif a == '*':
            stack.append(c * d)
        else:
            stack.append(c / d)
    else:
        stack.append(float(a))
        
print(f"{stack[0]:.6f}")

2021fall-cs101,周稚坤。

逆波兰是运算符号尽量往后。https://en.wikipedia.org/wiki/Reverse_Polish_notation 转化为逆波兰表达式更有利于理解“栈”的概念,通过这个题我对栈的理解有了一定加深。首先要明确逆波兰表达式的基本运算为: (逆波兰表达式1)算符(逆波兰表达式2)=(逆1)(逆2)算符 重复操作后会发现这是一个树的结构,那么其中一种方法就是以一个树根开始一步步向上走,并将遇上的数堆到栈里。每当遇上符号,则进行一次上述运算并拿走栈上最上面的两个值,以此往复到最后自然只剩下运算结果,输出即可。

这里第一次知道eval 函数用来执行一个字符串表达式,并返回表达式的值。 尝试学习了李文梁同学的递归代码,自己认为这和“递归的本质就是不要关心递归的那个函数究竟发生了什么”有很大关联。只要记住最基本的(逆波兰表达式1)算符(逆波兰表达式2)=(逆1)(逆2)算符 就好。

python
# http://cs101.openjudge.cn/practice/02694/

D=input().split()
D.reverse()
def STACK(D):
    stack=[]
    for i in D:
        if i in '+-*/':
            a=stack.pop()
            b=stack.pop()
            stack.append(str(eval(a+i+b)))
        else:
            stack.append(i)
    return stack[0]
print('%6f'% float(STACK(D)))

2022fall-cs101,余力圣,化学与分子工程学院。正则表达式解法,可以在这里验证,https://regex101.com

image-20221127163848122

python
import re
sig = re.compile(r'[\+\-\*/][ ][^ \+\-\*/]+[ ][^ \+\-\*/]+')

def cal(s):
    par = re.search(sig, s)
    if par == None: 
        return s
    
    p = str(par.group()).split()
    #print(p)
    s = re.sub(sig, str(eval(p[1]+p[0]+p[2])), s, count=1)
    #print(s)
    #print()
    return cal(s)

x = input()
print('%f'%(float(cal(x))))