The most straightforward solution is to write a recursive function that calls itself to evaluate the inner expressions.
# helper function to parse strings where necessary
def intorfloat(number):
if not isinstance(number, str): # already parsed
return number
try:
return int(number)
except ValueError:
return float(number)
# evaluates a simple expression (no subexpressions)
def evaluate2(operation, operand1, operand2):
operand1, operand2 = intorfloat(operand1), intorfloat(operand2)
if operation == "*":
return operand1 * operand2
elif operation == "+":
return operand1 + operand2
elif operation == "/":
# keep the result an int if possible
result1 = operand1 // operand2
result2 = float(operand1) / operand2
return result1 if result1 == result2 else result2
elif operation == "-":
return operand1 - operand2
# recursively evaluate an expression with subexpressions
def evaluate(expression):
operation, operand1, operand2 = expression
if isinstance(operand1, list):
operand1 = evaluate(operand1)
if isinstance(operand1, list):
operand2 = evaluate(operand2)
return evaluate2(operation, operand1, operand2)
An iterative solution is also possible; the advantage is that you can evaluate expressions of any depth. In this version, we keep descending into sublists until we find a leaf: an expression that doesn't have any subexpressions. Then we evaluate the leaf and replace it with its result, and start over from the beginning. Eventually the top-level expression has no subexpressions.
This algorithm actually modifies the expression, repeatedly simplifying it until it's trivial to evaluate it. (The recursive solution does not modify the expression.)
If the expression is deep, we may do a lot of unnecessary traversal to repeatedly find leaf nodes. We could create a stack to allow us to backtrack instead of having to start at the top each time, but we might as well just use the recursive solution at that point.
# uses intorfloat() and evaluate2() functions previously shown
def evaluate(expression):
while isinstance(expression[1], list) or isinstance(expression[2], list):
current = expression
container = None
while True: # find a leaf node
operation, operand1, operand2 = current
if isinstance(operand1, list):
container, slot = current, 1
current = operand1
elif isinstance(operand2, list):
container, slot = current, 2
current = operand2
else:
break
if container:
container[slot] = evaluate2(*current)
return evaluate2(*expression)
print evaluate(['*', ['*', ['*', '5', '8'], '9'], '10']) # 3600