FizzBuzz done right as a programming problem

FizzBuzz is a classic programming question often given at interview. The question goes like this:

Produce code that will yield a list of numbers from 0 to 100 except that it will yield 'Fizz' for each number divisible by three, 'Buzz' for each number divisible by five and 'FizzBuzz' for each number divisible by both three and five.

Its a 'nice' task because it makes you think. Such thinking should be done in code, and when my head can't hold it all, I use the shell (which is sorta the point of this post):

(cccs)~/wk/cccs/core $ ipython
Python 2.7.6 (default, Mar 22 2014, 22:59:56) 
Type "copyright", "credits" or "license" for more information.

IPython 2.0.0 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: [i for i in range(16)]
Out[1]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]

In [2]: ['Fizz' if i%3 == 0 else i for i in range(16)]
Out[2]: 
['Fizz',
 1,
 2,
 'Fizz',
 4,
 5,
 'Fizz',
 7,
 8,
 'Fizz',
 10,
 11,
 'Fizz',
 13,
 14,
 'Fizz']

In [3]: ['Fizz' if i%3 == 0 else 'Buzz' if i%5 == 0 else i for i in range(16)]
Out[3]: 
['Fizz',
 1,
 2,
 'Fizz',
 4,
 'Buzz',
 'Fizz',
 7,
 8,
 'Fizz',
 'Buzz',
 11,
 'Fizz',
 13,
 14,
 'Fizz']

In [4]: [('FizzBuzz' if i%5 == 0 else 'Buzz') if i%3 == 0 else 'Buzz' if i%5 == 0 else i for i in range(16)]
Out[4]: 
['FizzBuzz',
 1,
 2,
 'Buzz',
 4,
 'Buzz',
 'Buzz',
 7,
 8,
 'Buzz',
 'Buzz',
 11,
 'Buzz',
 13,
 14,
 'FizzBuzz']
In [5]: ['FizzBuzz' if i%15 == 0 else 'Buzz' if i%3 == 0 else 'Buzz' if i%5 == 0 else i for i in range(16)]
Out[5]: 
['FizzBuzz',
 1,
 2,
 'Buzz',
 4,
 'Buzz',
 'Buzz',
 7,
 8,
 'Buzz',
 'Buzz',
 11,
 'Buzz',
 13,
 14,
 'FizzBuzz']

Once you've cracked a problem in the shell, you can integrate it nicely into your code. This works with big complex problems too where you can import e.g. the Django models and objects you need into the shell and play with them to think through your coding problems.

In this case, it yields a nice quick and reasonably clear bit of code that solves the problem. Of course, there are refinements and other solutions. In an ideal world it is good to be able to reflect on the problem at this stage and see if we really have a good solution but often, in the world of freelancing, you have to cede to the time pressure and move on.

That said...

What if the problem had been posed with different parameters or the poser decided, like Sheldon with 'rock, paper, scissors' in BBT to make the game more interesting, turning it into "FizzBuzzBang" where Bang is used and appended if the number is divisible by ten. Now we have a specification which can be expressed like this:

In [1]: simple_game = ((3, "Fizz"),(5, "Buzz"))

In [3]: interesting_game = ((3, "Fizz"), (5, "Buzz"), (10, "Bang"))

Now our problem is more interesting so we focus on its parts - lets process the integer into a result with a "fizzify" function first. Time to make a file - ipython's editing is slick but I still prefer to use a file once things get interesting.

Contents of fizzbang.py:

def fizzify(n, specs):
    """
    Convert number to appropriate representation for fizzify game
    :param n: number to convert
    :param specs: tuple of tuples ((denominator, label)...)
    :return: n or conversion made of labels as appropriate
    """
    conversion = ""
    converted = False
    for (denominator, label) in specs:
        if n % denominator == 0:
            conversion += label
            converted = True
    return conversion if converted else n

Looks good. Quickly written in part thanks to playing with the initial algorithm. Lets test it:

In [8]: run fizzbang.py

In [9]: fizzify(0, simple_game)
Out[9]: 'FizzBuzz'

In [10]: fizzify(1, simple_game)
Out[10]: 1

In [11]: fizzify(30, interesting_game)
Out[11]: 'FizzBuzzBang'

Cool. Now, we generate our values thus:

In [16]: [fizzify(n, interesting_game) for n in range(31)]
Out[16]: 
['FizzBuzzBang',
 1,
 2,
 'Fizz',
 4,
 'Buzz',
 'Fizz',
 7,
 8,
 'Fizz',
 'BuzzBang',
 11,
 'Fizz',
 13,
 14,
 'FizzBuzz',
 16,
 17,
 'Fizz',
 19,
 'BuzzBang',
 'Fizz',
 22,
 23,
 'Fizz',
 'Buzz',
 26,
 'Fizz',
 28,
 29,
 'FizzBuzzBang']

The ipython shell is not just an occasional tool, it is a python thinking aid which I use constantly when I'm working with Python code.


Comments

There are currently no comments

New Comment

required

required (not published)

optional

Australia: 07 3103 2894

International: +61 410 545 357

Feeds

RSS / Atom