2

I'm playing around with pushing Python (2.7, via the pythonista iOS interpreter) to do some weird functional things. Specifically, I'm trying to implement a one-line fizzbuzz using nested if-else lambdas and map. But I'm new to this kind of dirty trick, and it isn't going so 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 the list elements aren't passed through to the inner lambdas (per this SO: Scope of python lambda functions and their parameters ). Ok, so then I tried nesting the maps in 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

That at least produced values, but obviously 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 has run out. Obviously, the repeated calls to map are evaluating the whole list over and over, but if there's no way to get the variables to the nested lambdas without it, am I stuck? I suppose there might be some solution involving mutating the list, like deleting the list item every time a lambda returns a value, but that seems unreasonably complex as well as totally un-functional. I am so close to a one-line functional fizzbuzz! Does anyone have any clues here?

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

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

Community
  • 1
  • 1
Paul Gowder
  • 2,409
  • 1
  • 21
  • 36

4 Answers4

3

The problem is that you aren't calling the inner lambdas. An expression like a if b else lambda: ... just returns the lambda itself (i.e., a function object) if the condition is false. You can get your result with this:

>>> 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 need to do all that just to get a one-line fizzbuzz. You can just directly nest the if/else expressions inside each other in a single 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 could just a list comprehension:

>>> 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']
BrenBarn
  • 242,874
  • 37
  • 412
  • 384
2

If I could suggest a slightly different approach which I'd consider more pythonic. List comprehensions also express functional operations, but less verbosely 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]

It makes it clearer what's going on in my opinion.

machine yearning
  • 9,889
  • 5
  • 38
  • 51
1

I believe what you want is this:

claw = map(lambda x: 'fizzbuz' if x%15==0 else 'fizz' if x%3==0 else 'buzz' if x%5==0 else x, alist)
Hai Vu
  • 37,849
  • 11
  • 66
  • 93
1

Your issue is operator precedence. You can see this more clearly if you replace all the lambdas after the first with strings, 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 already posted an answer that shows a way to do this with multiple lambdas, but a better way to do this while still using map and a lambda is just this:

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

Though I would rather use a list comprehension even if you insist on using the 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 actually 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]
Cyphase
  • 11,502
  • 2
  • 31
  • 32