表达式-> 逆波兰表达式-> 求值

19 Oct 2015

equation

普通表达式转换成逆波兰序列的代码太复杂了. 需要改改..

import re
# inspired by:
# https://en.wikipedia.org/wiki/Reverse_Polish_notation
# https://en.wikipedia.org/wiki/Shunting-yard_algorithm

tests = [
    '12 + 2 * 10 -11 * 2 / 1', # 10
    '2 * 3 + 6 / 2', # 9
    '(1+2)*3',
    '1*(1+2*3)*4',
    '(2-(4+8*3))-9',
    '((1-2)+3)', # broken
    '(((3-1)))',
    '((3-1)+(3-1))'
]

OPS = ('+', '-', '*', '/', '(', ')')
OP_LEVEL = { '+': 1, '-': 1, '*': 2, '/': 2, '(': 3, ')': 3 }

def op_helper(j, i, op):
    if (op == '+'):
        return i+j
    elif (op == '-'):
        return i-j
    elif (op == '*'):
        return i*j
    elif (op == '/'):
        return i/j

def string_to_arr(expr):
    expr = expr.replace(' ', '')
    store = []
    tmp = []
    for x in expr:
        if x in OPS:
            if tmp:
                store.append(int(''.join(tmp)))
            store.append(x)
            tmp = []
        else:
            tmp.append(x)
    else:
        if tmp:
            store.append(int(''.join(tmp)))

    return store


def transform_to_reverse_polish(expr):
    expr = string_to_arr(expr)

    tokens = []
    output = []

    for ele in expr:
        if ele in OPS:
            # print 'output', output
            # print 'tokens', tokens
            if not tokens:
                tokens.append(ele)
                continue

            last_op = tokens[-1]

            if OP_LEVEL[ele] > OP_LEVEL[last_op]:  # [+] <- '*'
                tokens.append(ele)
                if ele == ')': # pop tokens until encounter with '('
                    tokens.pop()
                    while(tokens):
                        t = tokens.pop()
                        if t != '(':
                            output.append(t)
                        else: # t == '('
                            break

            elif OP_LEVEL[ele] == OP_LEVEL[last_op]: # [+ *] <- '/''
                if ele == '(':
                    tokens.append(ele)
                elif ele == ')':
                    while(tokens):
                        t = tokens.pop()
                        if t != '(':
                            output.append(t)
                        else: # t == '('
                            break

                else:
                    output.append(tokens.pop())
                    tokens.append(ele)

            elif OP_LEVEL[ele] < OP_LEVEL[last_op]: # [+ *] <- '-'
                while (tokens):
                    t = tokens.pop()
                    if t == '(':
                        tokens.append(t)
                        tokens.append(ele)
                        break
                    if OP_LEVEL[t] >= OP_LEVEL[ele]:
                        output.append(t)
                    else:
                        tokens.append(t)
                        tokens.append(ele)
                        break
                else:  # if [] is empty
                    tokens.append(ele)

        else: # not token ,just push into output
            output.append(int(ele))

    else:
        # print 'rest tokens', tokens
        while (tokens):
            tk = tokens.pop()
            output.append(tk)

    return output

def calc(polish_list):
    # input polish_list must be validated.
    calc_list = []

    while (polish_list):  # util list drianed
        token = polish_list.pop(0)
        if token in OPS:
            v1 = calc_list.pop()
            v2 = calc_list.pop()
            calculated_value = op_helper(v1, v2, token)
            calc_list.append(calculated_value)
        else:
            calc_list.append(token)

    return calculated_value


if __name__ == "__main__":
    for t in tests:
        rpl = transform_to_reverse_polish(t)
        # print 'rpl',rpl
        print calc(rpl) == eval(t)