Kerry Buckley

What’s the simplest thing that could possibly go wrong?

First stab at Python

4 comments

Monty Python foot “It is an ex parrot. It has ceased to be.”

No, of course not. I’m talking about the other Python. Not the one with the silly walks, dead parrot and singing lumberjacks, but the one with the idiosyncratic approach to whitespace.

One of the APIs available for the Web21C SDK is Python, and there was recently some concern that we didn’t have enough expertise in the team to continue providing technical support for the Python SDK. A bunch of us have volunteered to start learning the language, and as a first step, Tim set us some homework:

Write a program that processes a list of numbers from 1 to 100. For each number, if the number is a multiple of 3, print “FIZZ”; if the number is a multiple of 5, print “BANG”; otherwise, print the number.

You are *NOT* allowed to use any *IF/ELSE* statements in your code. You can use the list-accessing ternary operator hack, but whilst I’ll accept your homework if you do, you’ll miss out on the prize (alcoholic), which goes to the most concise code (not including whitespace).

Now I have no idea what this mysterious ‘list-accessing ternary operator hack’ might be, but it sounds painful (as does missing out on an alcoholic prize), so it looks like I need to eschew ifs completely.

Since I’ve never written a line of Python in my life, I decided the easiest way to proceed would be to start off by figuring out enough syntax to solve the problem as defined by the first paragraph, then write a test which the simple code passes, then factor out the conditional logic. Normally I’d start with the test, but I think learning a new language is one of the cases where TDD isn’t really appropriate.

So here’s my first working code:

for n in range(1, 100):
	if n % 3 == 0:
		if n % 5 == 0:
			print 'FIZZBANG'
		else:
			print 'FIZZ'
	elif n % 5 == 0:
		print 'BANG'
	else:
		print n

This prints out what you would expect:

PyMate r8111 running Python 2.5.1 (/usr/bin/env python)
>>> fizzbang.py

1
2
FIZZ
4
BANG
FIZZ
7
8
FIZZ
BANG
11
FIZZ
13
14
FIZZBANG
16
...

OK, so far so good. The next step was to find out what the Pythonistas use for unit testing. As a card-carrying BDD and RSpec fan, I was initially interested to see that there’s a PySpec, but at first glance it doesn’t look that great (maybe it’s just that I’m not used to reading Python code). Then I came across the bundled DocTest, and while I’m not entirely convinced by its description on the Wikipedia BDD page as ‘BDD for Python’, it looked ideal for the task at hand.

Here’s the same code, but with the addition of a parameter to specify the number to count up to, and a DocTest test:

#!/usr/bin/env python

"""
Print numbers up to limit, replacing those divisible by 3 and/or 5 with FIZZ
and/or BANG.

>>> fizzbang(20)
1
2
FIZZ
4
BANG
FIZZ
7
8
FIZZ
BANG
11
FIZZ
13
14
FIZZBANG
16
17
FIZZ
19
BANG
"""
def fizzbang(limit):
	for n in range(1, limit + 1):
		if n % 3 == 0:
			if n % 5 == 0:
				print 'FIZZBANG'
			else:
				print 'FIZZ'
		elif n % 5 == 0:
			print 'BANG'
		else:
			print n

def _test():
   import doctest
   doctest.testmod()

if __name__ == "__main__":
   _test()

OK, now to start thinking about the hard bit – getting rid of those ifs. As a first step, I got rid of the nested logic by appending the ‘FIZZ’ and/or ‘BANG’ to a temporary string, then either printing that string, or the original number if it was empty:

def fizzbang(limit):
	for n in range(1, limit + 1):
		out = ''
		if n % 3 == 0:
			out += 'FIZZ'
		if n % 5 == 0:
			out += 'BANG'
		print out or n

At this point, I hit on the idea of using arrays to select either an empty string or the appropriate word:

def fizzbang(limit):
	for n in range(0, limit):
		print ['', '', 'FIZZ'][n%3] + ['', '', '', '', 'BANG'][n%5] or n + 1

Now all that’s left is to remove the limit parameter so the code merely satisfies the original criteria:

for n in range(100):
	print ['', '', 'FIZZ'][n%3] + ['', '', '', '', 'BANG'][n%5] or n + 1

I expect there are other more concise (and no doubt more efficient) solutions – I look forward to seeing what the other ‘pupils’ come up with!

Technorati Tags: , , ,

Written by Kerry

April 14th, 2008 at 8:44 am

Posted in Software

4 Responses to 'First stab at Python'

Subscribe to comments with RSS or TrackBack to 'First stab at Python'.

  1. You got my uri wrong, it’s http://tim-watson.blogspot.com.

    That’s very good, well done. Now try it without *any* conditional logic (i.e., remove the ‘or’ from the end).

    Nice work!

    Tim Watson

    14 Apr 08 at 12:29 pm

  2. Hint: look up the keyword, ‘lambda’. :)

    Tim Watson

    14 Apr 08 at 12:34 pm

  3. Well done, my solution (if my blog comes back up I may actually get to post it there) is much much less concise (although it contains no or). Let’s see what the master will make of it…

    Yannis

    14 Apr 08 at 12:38 pm

  4. You got my uri wrong

    Oops, copy & paste error. Now fixed.

    Now try it without *any* conditional logic

    Hey, no fair! You’ve already set this week’s homework ;-)

    Kerry

    14 Apr 08 at 1:53 pm

Leave a Reply

You must be logged in to post a comment.