Reputation: 35
I'm trying to build a python function from a input given in Reverse Polish notation. My function is supposed to be able to take in x
and y
values as parameters and return the value of the function for the respective x
and y
.
My input is for example:
"x 1 + y 3 * +"
My expected result would be a function object similar to:
fun = lambda x,y: x+1+y*3
So that I can call fun(1,2)
and get 9
as the result.
So far I can evaluate input without variables and give the result.
bin_ops = {
"+": (lambda a, b: a + b),
"-": (lambda a, b: a - b),
"*": (lambda a, b: a * b),
"/": (lambda a, b: a / b),
}
def eval(expression):
tokens = expression.split()
stack = []
for token in tokens:
if token in bin_ops:
arg2 = stack.pop()
arg1 = stack.pop()
result = bin_ops[token](arg1, arg2)
stack.append(result)
else:
stack.append(float(token))
return stack.pop()
txt = '2 1 + 2 3 * +'
print(eval(txt)) # prints 9.0
Can you help me how to build a function instead of directly the result
and how I can process variables (such as x
and y
) properly?
Upvotes: 2
Views: 494
Reputation: 104752
You can change your parsing code from creating values into creating functions that can be called with keyword arguments for final evaluation:
bin_ops = {
"+": lambda a, b: lambda **namespace: a(**namespace) + b(**namespace),
"-": lambda a, b: lambda **namespace: a(**namespace) - b(**namespace),
"*": lambda a, b: lambda **namespace: a(**namespace) * b(**namespace),
"/": lambda a, b: lambda **namespace: a(**namespace) / b(**namespace),
}
def eval(expression):
tokens = expression.split()
stack = []
for token in tokens:
if token in bin_ops:
arg2 = stack.pop()
arg1 = stack.pop()
result = bin_ops[token](arg1, arg2)
stack.append(result)
else:
try:
num = float(token)
except ValueError:
stack.append(lambda *, _token=token, **namespace: namespace[_token])
else:
stack.append(lambda *, _num=num, **namespace: _num)
return stack.pop()
txt = "x 1 + y 3 * +"
fun = eval(txt)
print(fun(x=2, y=4)) # prints 15.0
The way Python scopes work makes it a little tricky to generate functions that use variables from a loop. This is why I use token
and num
to set default values for arguments in our generated lambda
functions, rather than using their names directly. If you just did lambda **namespace: namespace[token]
, you'd find that token
had changed value as the loop kept running. The bin_ops
code doesn't have that issue because the final functions are already nested in an extra scope, since the lambda a, b
function is where we build the inner lambda
s.
Upvotes: 1