It has been very hard to use recursion, but I think that it made the code shorter and cleaner.
import doctest
import operator as op
START_FOR_SYMBOLS = {'+': 0,
'*': 1,
'/':1,
'-':0
}
OP_FOR_SYMBOL = {'+': op.add,
'*': op.mul,
'/': op.truediv,
'-': op.sub
}
def innermost_parens(text):
"""
Returns the text inside the innermost parenthesis.
>>> innermost_parens("1 + (2 * (4 - 1))")
'4 - 1'
>>> innermost_parens("1 + (2 * (4 * (2 + (8 * 7)) - 1))")
'8 * 7'
"""
if not '(' in text and not ')' in text:
return text
open_ = text.index('(')
close_ = text.rindex(')')
return innermost_parens(text[open_+1:close_])
def polish_eval(expr,start=None):
"""
Unlimited polish eval, works for any number of arguments.
>>> polish_eval('+ 4 1 6')
11.0
>>> polish_eval('* 4 5 5')
100.0
"""
tokens = expr.split(' ')
if start is None:
start = START_FOR_SYMBOLS[tokens[0]]
if len(tokens) == 1:
return start
return polish_eval(' '.join(tokens[:-1]),
start = OP_FOR_SYMBOL[tokens[0]](start,float(tokens[-1]))
)
def infix_eval(expr):
"""
Reduced infix eval, only works with 2 numbers.
>>> infix_eval('9 + 4')
13.0
>>> infix_eval('2 * -6')
-12.0
"""
a, oper, b = expr.split()
return OP_FOR_SYMBOL[oper](float(a),float(b))
def full_eval(expr, eval_type):
"""
Evals by the rules of eval_type starting from the inner
parenthesis.
>>> full_eval("(* 4 5 (+ 4 1))", polish_eval)
100.0
>>> full_eval("(* 4 (/ 10))", polish_eval)
0.4
>>> full_eval("(1 + (5 * 2))", infix_eval)
11.0
"""
if len(expr.split(' ')) == 1:
return float(expr)
inn = innermost_parens(expr)
new_expr = expr.replace('('+str(inn)+')',str(eval_type(inn)))
return full_eval(new_expr, eval_type)
def interface():
which_expr = input("Polish or infix? ")
if 'polish' in which_expr.lower():
evaller = lambda expr: full_eval(expr, polish_eval)
else:
evaller = lambda expr: full_eval(expr, infix_eval)
while True:
result = evaller(input('> '))
print(result)
if __name__ == "__main__":
doctest.testmod()
interface()