18
Oct
12

FizzBuzz

FizzBuzz is variously; a drinking game, a maths game, a way of learning numbers in a foreign language and more importantly (for this blog) a programming kata. The principle of FizzBuzz is a counting and substitution game you count to a limit and replace some numbers with words. In the classic example you replace any number which is divisible by 3 with Fizz and any number divisible by 5 with Buzz, and any number which is divisible by both with FizzBuzz.

So a classic game to 20 looks like this:

    1
    2
    Fizz
    4
    Buzz
    Fizz
    7
    8
    Fizz
    Buzz
    11
    Fizz
    13
    14
    FizzBuzz
    16
    17
    Fizz
    19
    Buzz

It is fairly trivial to make a program to print lists like this out and can be done with some basic programming skill. This is a python function which, given a limit, will print numbers under the limit with the required replacements.

#==============================================================================
def fizz_buzz(limit):
    """ 
    For the each number in 1 - limit inclusivly, this function prints either;
    the number, Fizz if it is divisible by 3, Buzz if it is divisible by 5 and
    FizzBuzz if it's divisible by both.
    """
    for i in range(1, limit + 1):
        if (i % 15 == 0):
            print("FizzBuzz")
        elif (i % 5 == 0):
            print("Buzz")
        elif (i % 3 == 0):
            print("Fizz")
        else:
            print(i)

This is a nice and simple function that can be followed fairly easily. It loops over the values 1 – limit inclusively, if the number is divisible by 3 and 5 it prints FizzBuzz, if the number is divisible by 5 it prints Buzz, if it is divisible by 3 it prints Fizz, and if none of the above it prints the number.

This is all well and good, but it’s when you are asked to expand the rules that this approach falls down. For instance this only has one special case but if you want to say Pop rather than a multiple of 7 then the function becomes this.

#==============================================================================
def fizz_buzz_pop(limit):
    """ 
    For the each number in 1 - limit inclusivly, this function prints either;
    the number, Fizz if it is divisible by 3, Buzz if it is divisible by 5, Pop
    if it's divisible by 7, FizzBuzz if it's divisible by both 3 and 5, FizzPop
    if it's divisible by both 3 and 7, BuzzPop if it's divisible by 5 and 7 and
    FizzBuzzPop if it's divisible by 3, 5 and 7.
    """
    for i in range(1, limit + 1):
        if (i % 105 == 0):
            print("FizzBuzzPop")
        elif (i % 35 == 0):
            print("BuzzPop")
        elif (i % 21 == 0):
            print("FizzPop")
        elif (i % 15 == 0):
            print("FizzBuzz")
        elif (i % 7 == 0):
            print("Pop")
        elif (i % 5 == 0):
            print("Buzz")
        elif (i % 3 == 0):
            print("Fizz")
        else:
            print(i)

And of course if you say that multiples of 11 become Bizz you get into real trouble.

I feel a more generic approach is needed here. Here is a fairly simple class called AdvancedFizzBuzz which will give us a fully extensible FizzBuzz machine.

#==============================================================================
class AdvancedFizzBuzz(object):

    def __init__(self, mapping):
        """ 
        mapping should be a dictionary with numbers as keys and what to print 
        as what it is mapped to. 
        
        e.g FizzBuzz is using a dictionary with:
          mapping[3] = Fizz
          mapping[5] = Buzz
        """
        self.map = mapping

    def __getitem__(self, index):
        ret_val = ""
        modified = False
        for key in self.map:
            if (index % key == 0):
                modified = True
                ret_val += self.map[key]
        if (not modified):
            ret_val = index
        return ret_val

    def run(self, limit):
        for i in range(1, limit + 1):
            print(self[i])

The constructor takes a dictionary which maps numbers to what to print instead of their multiples. This is fairly straight forward and as it says in the doc string if you give it a dictionary with mapping[3] = Fizz, mapping[5] = Buzz it will be FizzBuzz.

The function __getitem__() is more mysterious if you are not familiar with pythons magic methods. If you aren’t then I hugely recommend this guide, it has helped me enormously. What this function does is make your class indexable, just like a list or dictionary.

What the function actually does is the work of this class. It looks through all it’s rules (they keys in it’s map) and finds if the number it has been given is divisible by them. If it is divisible then it will append it’s key word onto the string to be returned and also mark the the value as modified. If the value has been modified it returns the string otherwise it returns the number itself.

The run() function is self-explanatory it goes through 1 – limit inclusively and prints the return value from the previous function.

This means that this class does not need editing to extend it’s functionality. I simply provide a different dictionary of rules. The results of running the function fizz_buzz_pop(110) and using the class with the dictionary {3: ‘Fizz’, 5: ‘Buzz’, 7: ‘Pop’} are given as text files in this repository. I used 110 so that it got to at least the first FizzBuzzPop which is at 105.

And of course this means that rather than writing a new function for multiples of 11 being Bizz you use the dictionary {3: ‘Fizz’, 5: ‘Buzz’, 7: ‘Pop’, 11: ‘Bizz’}.

The source code used in this article can be found in this repository.

Advertisements

0 Responses to “FizzBuzz”



  1. Leave a Comment

What did you think?

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


%d bloggers like this: