Map, Filter, Reduce…

Functions famous from functional programming are map, filter and reduce. In Python they’re not actively promoted because there are better alternatives, but I ran into them recently in a coding challenge. The challenge was basically: write a function that returns True if the sum of the ascii (or unicode) values of the input string is odd.

To get the ascii value of a character, we use the function ord (for “ordinal):

>>>print(ord('A'))
65

So my initial solution looked like this; it’s not exactly rocket science:

def that_is_odd(text):
    return sum(ord(c) for c in text) % 2 == 1

Pretty simple; take each character in the text in a list comprehension, calculate the ordinal, and take the sum. Take the remainder of dividing by two, and if that’s 1, the sum is odd.

The one “problem” with this solution is that in these contests a shorter solution is better. Luckily, white space and comments are not counted, but that still doesn’t reward clean code. So, I was off a considerable amount of characters from the “optimal” solution. Where to squeeze?

Well, first off, your input parameter doesn’t have to be text; it can simply be t. And the modulo function returns either 0 or 1, which if compared against what the outcome should be matches False or True (if the challenge engine tests 0==False or 1==True both results will pass). So we can have a shorter result:

def that_is_odd(t):
    return sum(ord(c) for c in t) % 2

But that wasn’t good enough! Still too long? What’s next? Well, I learned in a previous challenge that if your function can be written as a one-liner, you can replace the function definition with a lambda abomination:

that_is_odd = lambda t:sum(ord(c) for c in t) % 2

That’s a big step forward (in reducing character count; not in making a readable function!) and we’re almost there. I couldn’t figure out the last step and had to peek at the code of one of top 25 entries (who all had the same length):

that_is_odd = lambda t:sum(map(ord, t)) % 2

And that is as short as it gets.

What IS map?

Well, map does exactly what the list comprehension does; take all values from an interable, and apply a function to them. Why use it? Well, it’s there! Mainly because it was there in the early days of Python when list comprehensions didn’t exist.

In Python 2 there was still a good reason to use map; it was executed lazily, so when running it over an iterable with a million elements, memory use would be far less. But Python 3 treats list comprehensions as iterables as well, and not as lists.

For that reason, map and its cousins, filter and reduce are no longer preferred. Reduce is completely gone as a function in Python, and while map and filter are still there, both of them can be perfectly replaced by list comprehensions.

Never done a filter in a list comprehension? If we only want the ordinals for letters and nothing else, the calculation would look like this:

sum(ord(c) for c in text if c.isalpha())

Which is easily understandable if you’ve never seen it; unlike

sum(map(ord, filter(str.isalpha, text)))

Which is only understandable to a trained eye. And that’s why list comprehensions are preferred.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s