2

I have a list of m elements and n different numbers that appear in a list of strings. The list follows the following format example

eq = ['12', '2', '3', '-123', '-1', 'X']

Note, X represents the constant 1, other than the variable 1. I want to be able to convert this to a lambda function that takes in a list x and acts like this list was summed together. For our example the function would be

f(x) = x[1]*x[2] + x[2] + x[3] - x[1]*x[2]*x[3] - x[1] + 1

I know how to do this with a normal function, but I wanted to get it to work with a lambda and hit a wall. Any help would be appreciated!

Here is the function that works for the problem:

def evaluateList(x, lst):
    num = 0
    for term in lst:
        if term[0] == "X":
            num += 1
        elif term[0] == "-":
            num -= reduce(lambda x,y: x*y, [x[int(y)-1] for y in term[1:]])
        else:
            num += reduce(lambda x,y: x*y, [x[int(y)-1] for y in term])
    return num
4
  • Can you show the function definition that works for your problem? Commented Jun 23, 2018 at 17:17
  • @Carcigenicate unless there's state involved, which depending on implementation there could be Commented Jun 23, 2018 at 17:18
  • @BowlingHawk95 I just added the working function implementation. Thank you for the advice Commented Jun 23, 2018 at 17:20
  • @wjmccann the reason you're having trouble is (as mentioned above), you can't have state in lambda's. Because num is changing value over iterations, that implementation won't work in a lambda. But all is not lost! Any stateful iterative algorithm can be translated to a stateless one using combinators like map, reduce, and filter. This algorithm will look quite different than your function above, but it is doable. Let me play with it and see what I can come up with. Commented Jun 23, 2018 at 17:22

4 Answers 4

3

I think this is simple enough:

from operator import mul
from functools import reduce
prod = lambda L: reduce(mul, L, 1)
evaluateList = lambda(x, eq): sum(
    1 if expr == 'X' 
    else prod(-1 if i == '-' 
              else x[int(i)] 
              for i in expr) 
    for expr in eq)
Sign up to request clarification or add additional context in comments.

1 Comment

Really wish functools.reduce used kwargs so you could simplify the prod assignment with functools.partial.
3

Clearly I recommend the def implementation:

from functools import reduce

eq = ['12', '2', '3', '-123', '-1', 'X']
x = [n for n in range(100)]

# Using an auxiliary function
def evaluate(n):
    if n == "X":
        return 1
    elif n[0] == "-":
        return reduce(lambda x, y: x * y, [x[int(y) - 1] for y in n[1:]])
    else:
        return reduce(lambda x, y: x * y, [x[int(y) - 1] for y in n])

print(sum(map(evaluate, eq)))

# As a pure lambda:

evaluate_lambda = lambda n: sum(map((lambda n: 1 if n == 'X' else (reduce(lambda x, y: x * y, [x[int(y) - 1] for y in n[1:]]) if n[0] == "-" else reduce(lambda x, y: x * y, [x[int(y) - 1] for y in n]))), eq))

print(evaluate_lambda(eq))

Comments

3

You can use functools.reduce twice, although the result is not very clean:

from functools import reduce
from operator import sub, add
eq = ['12', '2', '3', '-123', '-1', 'X']
_x = [6, 4, 1, 4, 5, 2, 4, 23, 2, 4]
final_result = reduce(lambda x, y:[add, sub][y.startswith('-')](
  reduce(lambda c, d:c*d, [_x[int(i)] for i in x]) if x in eq else x, 
  reduce(lambda c, d:c*d, [_x[int(i)] for i in (y[1:] if y.startswith('-') else y)] if y != 'X' else [1])
  ), eq)

print(final_result)
x = _x
print(x[1]*x[2] + x[2] + x[3] - x[1]*x[2]*x[3] - x[1] + 1)

Output:

-10
-10

But again, a predefined function is much easier to read and scale.

Also, slightly shorter, with sum:

new_result = sum([1, -1][i.startswith('-')]*reduce(lambda x, y:x*y, [1] if i == 'X' else [x[int(c)] for c in (i[1:] if i.startswith('-') else i)]) for i in eq)

Comments

2

that's a bit ugly but it works

from operator import mul
a = lambda eq: sum([reduce(mul, [int(x) if x.isdigit() else [1,-1][x=='-'] for x in s]) for s in eq])


print(a(['12', '2', '3', '-123', '-1', 'X']))  # outputs 1
print(a(['126', '32', '3', '-2', 'X']))  # outputs 20

edit: of course this is just a game. always use the most readable function

2 Comments

[1,-1][x=='-'] relies on the python quirk that True is secretly 1 and False is zero. That's really really hard to debug when buried in a large module IMO. Would avoid this if at all possible.
of course. but the whole lambda-ing that function is a game. so let's have fun

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.