Sunday, November 16, 2008

Teaching the Snake a New Trick

I learned Python about three years ago. At that time, I didn't know Python at all and took a job with a company that uses Python extensively. My job didn't actually consist of any Python work, but since I liked the company, I knew learning Python would be important for getting rehired. Python has since grown on me quite a bit, and whenever I am given a programming problem, I often find I come up with a Python solution the fastest. There are a few things that I love about it, but I'm not here to start a language war. I just taught the snake a new trick and I want to share it with you.

First, some background. I am a huge fan of the functional programming syntax that Python provides. Its for loops and list manipulation helpers are particularly natural and it's never been easier to mix and match imperative and functional programming.

One thing that I have been trying to do for a while but never actually found a Good Solution (TM) to is iterating over each consecutive pair of elements in a list. For example, if the list contains [1, 2, 3, 4], I want to execute some block of code with the pairs (1, 2), (2, 3), (3, 4).

The naive solution looks something like this:
L = [1, 2, 3, 4]
for i in range(len(L) - 1):
  a, b = L[i], L[i + 1]
  # work with a, b here

But come on, how ugly is that?

A much nicer solution uses the zip function as follows:
for a, b in zip(L[:-1], L[1:]):
  # work with a, b here

This forms two lists, one with the last element chopped off and one which is missing its first element. The pairs of corresponding elements of these two lists are the pairs of consecutive elements in the original list (zip does this part). Neat, huh? Can anyone come up with an even cleaner solution?

I leave you with a quote about programmers from Tidbits from the Dungeon:
Programmers are in a race with the Universe to create bigger and better idiot-proof programs, while the Universe is trying to create bigger and better idiots. So far the Universe is winning. -- Rich Cook
Well, it's back to homework for me.



Anonymous said...

While the fact that Python has some pseudo-functional features (just so that guy that created the language can claim the language is functional), it really lacks proper anonymous functions that truly functional languages like scheme feature. Perhaps you should read "Structure and Interpretation of Computer Programs" and then rethink this blog post.

-Pat said...

Good idea on the use of zip(), and I hate to do this to you: It seems you've discovered the exact reason zip() was created. Oh well, there's something to be said for independently discovering the same solution that has been implemented by the almighty language architects, eh?

Also, with a large enough list, you all of a sudden feel some memory pain using zip() (my first reaction), but thankfully the great architects foresaw this, so there is also itertools.izip() to be lazy and memory-friendly.