Craig
Craig

Reputation: 685

Python Mathematical Formula computation

I'm writing a python program to calculate formula. I read in a list of strings which hold the values, operators and functions.

The code shown below receives a string for example:

['not', 1.0, 2.0, '=', 'power', 2.0, 3.0, '+']

The code above is a postfix version of the mathematical problem: power(2,3)+not(2=1) The program should calculate not(2=1) first, resulting in 1 and then calculate power(2,3) giving 9 and then 8+0 resulting in a return of 8.

So far my code to calculate the answers

stack = []
    def calculate(inputs): 
        if (inputs[0] == "sum"):
        inputs.remove("sum")
    for a in inputs:
        if (a) == "not":
            inputs.remove(a)
            op1, op2 = inputs[0], inputs[1]
            inputs.remove(op1)
            inputs.remove(op2)
            if op1 != op2:
                stack.append('0')
            else:
                stack.append('1') 
            continue  
        if (a) == 'power':
            inputs.remove(a)
            continue       
        if type(a) is float:
            stack.append(a)
            continue 
        op1, op2 = stack.pop(), stack.pop()
        #if a == 'power':

        if a == '+':
            stack.append(op2 + op1)
        elif a == '-':
            stack.append(op1 - op2)
        elif a == '*':
            stack.append(op2 * op1)
        elif a == '/':
            stack.append(op1 / op2)
        elif a == '=':
            if op1 != op2:
                stack.append('0')
            else:
                stack.append('1')  


    if (len(stack) > 1):
        lenStack = len(stack)-1
        for x in range(0, lenStack):
            stack.append('+')
        stack.append(_calcSum(stack))
    return stack.pop()

def _calcSum(stack):
    newStack = []
    for a in stack:
        if type(a) is float:
            newStack.append(a)
            continue
        op1, op2 = newStack.pop(), newStack.pop()
        if a == '+':
            newStack.append(op2 + op1)
        elif a == '-':
            newStack.append(op1 - op2)
        elif a == '*':
            newStack.append(op2 * op1)
        elif a == '/':
            newStack.append(op1 / op2)
    return newStack.pop()

However I'm having trouble with the NOT and POWER statement; I can't figure out how to automatically check for these. Could anyone possibly point me in the right direction or assist with my code? When I try to check for 'power' it simply skips the rest of my code and attempts to print stack - which is empty causing an error.

Upvotes: 1

Views: 316

Answers (3)

Yann Vernier
Yann Vernier

Reputation: 15877

On the theme of redundant refinement, here's a variant of tobias_k's solution restructured for fewer special cases:

from operator import add, sub, mul, truediv, pow, eq, not_

ops = {"+": add, "-": sub, "*": mul, "/": truediv, 
       "power": pow, "=": eq, "not": not_}
# Mark how many inputs are needed
ops = {k:(1 if f is not_ else 2, f) for (k,f) in ops.items()}

def calculate(tokens):
    stack = []
    for token in tokens:
        try:
            args,func = ops[token]
            stack[-args:] = [func(*stack[-args:])]
        except KeyError:
            stack.append(float(token))
    return float(stack[-1])

Example:

>>> calculate("2 1 = not 2 3 power +".split())
9.0
>>> calculate("2 1 = not".split())
1.0
>>> calculate("2 3 power".split())
8.0

Yes, I'm using that Python's bool type is a subtype of int, so True is in fact 1. The difference of whether to produce 9 or 10 comes simply from the order of the power arguments (2**3==8 or 3**2==9).

The root of the problem remains that your original input expression isn't valid postfix notation, which makes evaluation order clear and therefore operator precedence redundant. If not comes before its argument, how can you tell when to evaluate it? not(1), not(1==2), and not((1==2)+(3**2) all look like possible interpretations, and it's no better for power.

Upvotes: 1

tobias_k
tobias_k

Reputation: 82889

Building upon IXI's answer, you can reduce the complexity and amount of code significantly by making use of functional programming: Create a mapping from symbols to operations to be performed, and then just look up the symbols in the input in that mapping. Doing this, you will see that power and not are not special at all, and additional unary and binary operators can be added with ease. You could even extend it to support ternary operators, such as ... if ... else ... (although you'd have to write them in postfix form).

BIN_OP = {"+": lambda x, y: x + y,
          "-": lambda x, y: x - y,
          "*": lambda x, y: x * y,
          "/": lambda x, y: x / y,
          "power": lambda x, y: x**y,
          "=": lambda x, y: int(x == y)}
UN_OP = {"not": lambda x: int(not x)}

def calculate(tokens):
    stack = []
    for token in tokens:
        if token in BIN_OP:
            op1, op2 = stack.pop(), stack.pop()
            operation = BIN_OP[token]
            stack.append(operation(op1, op2))
        elif token in UN_OP:
            op1 = stack.pop()
            operation = UN_OP[token]
            stack.append(operation(op1))
        else:
            stack.append(float(token))
    return stack.pop()

Example:

>>> calculate(['2', '1', '=', 'not', '2', '3', 'power', '+'])
10.0

Upvotes: 1

IXI
IXI

Reputation: 51

I think that the following code might be what you want:

import math

test_input = ['2', '1', '=', 'not', '2', '3', 'power', '+']

def calculate(input):
    newStack = []
    for a in input:
        print newStack

        if a == '+':
            op1, op2 = newStack.pop(), newStack.pop()
            newStack.append(op2 + op1)
        elif a == '-':
            op1, op2 = newStack.pop(), newStack.pop()
            newStack.append(op1 - op2)
        elif a == '*':
            op1, op2 = newStack.pop(), newStack.pop()
            newStack.append(op2 * op1)
        elif a == '/':
            op1, op2 = newStack.pop(), newStack.pop()
            newStack.append(op1 / op2)
        elif a == '=':
            op1, op2 = newStack.pop(), newStack.pop()
            if op1 == op2:
                newStack.append(1)
            else:
                newStack.append(0)
        elif a == 'not':
            op = newStack.pop()
            if op > 0:
                newStack.append(0)
            else:
                newStack.append(1)
        elif a == 'power':
            op1, op2 = newStack.pop(), newStack.pop()
            newStack.append(math.pow(op1, op2))
        else:
            newStack.append(float(a))

    return newStack.pop()

As PeterE pointed out in his comment your postfix code is wrong. I assume that your wanted postfix code probably is 2 1 = not 2 3 power +. Also note that the end value then would be 9+1 = 10 and not 8+0 = 8.

Wikipedia has a nice page on postfix code: http://en.wikipedia.org/wiki/Reverse_Polish_notation.

In the python code one function actually should be enough to push and pop all the needed stuff onto a stack. To achieve a basic implementation you can then simply check all your different operator cases and perform any needed operations. If none of the provided operators matches the current element, you can assume that the element is a numeric value and simply push the parsed float value.

Note that this is a very quick and dirty implementation, but it might lead you on the right track.

Upvotes: 1

Related Questions