2

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?

Blckknght
  • 100,903
  • 11
  • 120
  • 169
Simon
  • 101
  • 1
  • 6

1 Answers1

1

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 lambdas.

Blckknght
  • 100,903
  • 11
  • 120
  • 169
  • Thank you for your answer, and the explanation! I (need to) use Python 2 for my problem so I get a Error: "Single star parameter is not supported in Python 2", do you know a work around for this problem? – Simon Jul 16 '20 at 16:48
  • Ah, you can avoid that error by removing the `*, ` part from the last two `lambda` functions. But I'd also think about upgrading your Python version, Python 2 is no longer supported upstream. – Blckknght Jul 16 '20 at 18:11