Nested if-else lambdas in python returning a function object, not a value (passing a variable through?)

I'm playing around with Python push (2.7, via the pythonista iOS interpreter) to do some weird functional stuff. Specifically, I'm trying to implement a one-liner fizzbuzz using nested if-else lambdas and map. But I'm new to this dirty trick and things aren't going well.

Take the following code:

alist = [1, 2, 3, 15, 5]
claw = map(lambda x: 'Fizzbuzz' if x % 15 == 0 else lambda x: 'Fizz' if x % 3 == 0 else lambda x: 'Buzz' if x % 5 == 0 else x, alist)
print "claw"
print claw
print
claw2 = map(lambda x: 'scratch' if x == 1 else 2, alist)
print "claw2"
print claw2

      

This code produces the following output:

claw
[<function <lambda> at 0x3f19fb4>, <function <lambda> at 0x36ba534>, <function <lambda> at 0x3ffa3e4>, 'Fizzbuzz', <function <lambda> at 0x3ffaa74>]

claw2
['scratch', 2, 2, 2, 2]

      

After searching around, it seems likely that the problem in claw is that list items are not being passed to the inner lambdas (behind this SO: Scope of python lambda functions and their parameters ). So, I tried to nest cards too:

claw3 = map(lambda x: 'Fizzbuzz' if x % 15 == 0 else map(lambda x: 'Fizz' if x % 3 == 0 else map(lambda x: 'Buzz' if x % 5 == 0 else x, alist), alist), alist)
print "claw3"
print claw3

      

This at least produced values, but clearly not quite what I was trying to achieve:

claw3
[[[1, 2, 3, 'Buzz', 'Buzz'], [1, 2, 3, 'Buzz', 'Buzz'], 'Fizz', 'Fizz', [1, 2, 3, 'Buzz', 'Buzz']], [[1, 2, 3, 'Buzz', 'Buzz'], [1, 2, 3, 'Buzz', 'Buzz'], 'Fizz', 'Fizz', [1, 2, 3, 'Buzz', 'Buzz']], [[1, 2, 3, 'Buzz', 'Buzz'], [1, 2, 3, 'Buzz', 'Buzz'], 'Fizz', 'Fizz', [1, 2, 3, 'Buzz', 'Buzz']], 'Fizzbuzz', [[1, 2, 3, 'Buzz', 'Buzz'], [1, 2, 3, 'Buzz', 'Buzz'], 'Fizz', 'Fizz', [1, 2, 3, 'Buzz', 'Buzz']]]

      

And now my brain is over. Obviously repeated calls to the map are constantly checking the entire list, but if there is no way to get the variables in nested lambdas without it, am I stuck? I'm guessing there might be some sort of solution related to changing the list, such as deleting a list item every time the lambda returns a value, but this seems unnecessarily complicated and also completely non-functional. I am so close to the one-line functional fizzbuzz! Anyone have any clues here?

EDIT : Thanks y'all. For your collective entertainment / reward, some fully implemented one-liners fizzbuzzes:

https://gist.github.com/paultopia/d360116128c787e22ce8

+3


source to share


4 answers


The problem is you are not naming the inner lambdas. The type expression a if b else lambda: ...

simply returns the lambda itself (i.e. the function object) if the condition is false. You can get your result:

>>> claw = map(lambda x: 'Fizzbuzz' if x % 15 == 0 else (lambda x: 'Fizz' if x % 3 == 0 else (lambda x: 'Buzz' if x % 5 == 0 else x)(x))(x), alist)
>>> claw
[1, 2, 'Fizz', 'Fizzbuzz', 'Buzz']

      

However, you don't even have to do all that just to get the one-liner fizzbuzz. You can just directly nest expressions if/else

inside each other in one lambda:



>>> claw = map(lambda x: 'Fizzbuzz' if x % 15 == 0 else 'Fizz' if x % 3 == 0 else 'Buzz' if x % 5 == 0 else x, alist)
>>> claw
[1, 2, 'Fizz', 'Fizzbuzz', 'Buzz']

      

And you don't need any lambdas at all, you can just understand the list:

>>> claw = ['Fizzbuzz' if x % 15 == 0 else 'Fizz' if x % 3 == 0 else 'Buzz' if x % 5 == 0 else x for x in alist]
>>> claw
[1, 2, 'Fizz', 'Fizzbuzz', 'Buzz']

      

+2


source


If I could suggest a slightly different approach, which I would consider more pythonic. List enumeration also expresses functional operations, but less verbally in python. Try the following:

claw = ['Fizzbuzz' if x % 15 == 0
        else 'Fizz' if x % 3 == 0
        else 'Buzz' if x % 5 == 0
        else x for x in alist]

      



Clarifies what is going on in my opinion.

+2


source


I believe you want:

claw = map(lambda x: 'fizzbuz' if x%15==0 else 'fizz' if x%3==0 else 'buzz' if x%5==0 else x, alist)

      

+1


source


Your problem is operator priority. You can see this more clearly if you replace all lambdas after the first one with lines, and add parentheses to clarify:

>>> alist = [1, 2, 3, 15, 5]
>>> claw = map(lambda x: ('Fizzbuzz' if (x % 15 == 0) else "LAMBDA_FIZZ" if (x % 3 == 0) else "LAMBDA_BUZZ" if (x % 5 == 0) else x), alist)
>>> claw
[1, 2, 'LAMBDA_FIZZ', 'Fizzbuzz', 'LAMBDA_BUZZ']

      

BrenBarn has already posted an answer that shows a way to do this with multiple lambdas, but the best way to do it when using a map and a lambda is simply:

claw = map(lambda x: 'FizzBuzz' if (x % 15 == 0) else 'Fizz' if (x % 3 == 0) else 'Buzz' if (x % 5 == 0) else x, alist)

      

Although I would rather use a list comprehension, even if you insist on using a one-liner:

claw = ['FizzBuzz' if (x % 15 == 0) else 'Fizz' if (x % 3 == 0) else 'Buzz' if (x % 5 == 0) else x for x in alist]

      

This is how I would do it:

def fizzbuzz(x):
    if x % 15 == 0:
        return 'FizzBuzz'
    elif x % 3 == 0:
        return 'Fizz'
    elif x % 5 == 0:
        return 'Buzz'
    else:
        return x

claw = [fizzbuzz(x) for x in alist]

      

+1


source







All Articles