Rules Based Fizz Buzz


If you have interviewed at any point recently, then you might have come across the FizzBuzz problem. No, it is not to do with long term storage of your open cola bottle. It is based on a childhood game (I knew of a variant, not FizzBuzz) the idea is simple, you stand in a circle (or facing each other if you are just two), and you say a number, the others say the next number and so on. However, the tricky part is not to say certain numbers of multiples of those numbers. Here’s how a typical game would go… where 3=Fizz, and 5=Buzz

1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, FizzBuzz ...

How to solve it

The reasoning behind this problem is to check if you can use the Modulus operator. So what you are basically doing is dividing the current number with 3 or 5, if it is completely divisible, then you say Fizz if it is a multiple of 3, Buzz if it is a multiple of 5 and in Special cases where it is a multiple of 15 (3 and 5), like 15, 30, 45, 60, 75, 90 you say FizzBuzz.

Code Solution please

for i in 0...100 {
 if i % 3 == 0 { print("Fizz") }
 else if i % 5 == 0 { print("Buzz") }
 else { print(i) }
}

The problem with this code is that it skips the FizzBuzz, as an if would not execute the else part if the first conditional branch is satisfied.

so you would end up with multiple if’s with no else statements which would look something like this

for i in 0...100 {
    if i%3 == 0 { print("Fizz") }
    if i%5 == 0 { print("Buzz") }
    print(i)
}

This is all fine but you see that it also prints the number which is not what was expected. This is easily managed by using a boolean that indicates whether to print the number or not. The code is modified as

for i in 0...100 {
    var showNumber = true
    if i%3 == 0 { showNumber = false; print("Fizz") }
    if i%5 == 0 { showNumber = false; print("Buzz") }
    if showNumber == true { print(i) }
}

There are still little differences where ‘FizzBuzz’ instead of being printed as one for 15, it prints Fizz and Buzz on each line. This is easily resolved with using the terminator as “” instead of the default ‘\n’.

This is resolved by using print(“Fizz”, terminator: “”)

Rule based

Now, lets look at the topic of this article, managing FizzBuzz based on rules, such that you can modify it without changing it.
Lets look at another aspect of this, a bit into functional programming, where the principle is everything is a function. By definition, a function takes a parameter and based on that it does something that it is expected to. This is the S from the S.O.L.I.D. which stands for single responsibility. Anyways, this is doing something quite simpler.

We can create a table of rules and pass them to a function that will process accordingly. This might not be the fastest or the most optimised solution however it still demonstrates a simple way to use rules.

First let us create a function, to make this look and sound more authentic, let me say it looks like
f(x) -> process X and display accordingly

So let us write this function, which would comprise of the code we wrote earlier.

func process(_ index: Int) {
    var showNumber = true
    if index % 3 == 0 { 
        showNumber = false
        print("Fizz", terminator: "")
    }
    if index % 5 == 0 { 
        showNumber = false
        print("Buzz", terminator: "")
    }
    if showNumber == true {
        print(index, terminator: "")
    }
    print(";")
}

Now we can pass the number to this function as

for i in 0...100 {
    process(i)
}

The only problem is that the function is fixed, it is hard coded to process the numbers 3 and 5. What if we could change that and parameterize this so we could pass our own rules instead.

Let us create the rules,

let Rules: [Int: String] = [
    3: "Fizz",
    5: "Buzz",
]

and pass this to the function we wrote earlier. The function would look a little different such as

func process(_ index: Int, rules: [Int: String]) {
    if rules.count == 0 { 
        print(index)
        return
    }

    var showNumber = true
    rules.forEach { rule in
        let (number, label) = (rule.key, rule.value)
        if index % number == 0 {
            showNumber = false
            print(label, terminator: "")
        }
    }
    
    if showNumber == true {
        print(index, terminator: "")
    }
    
    print(";")
}

Now we can call the function with our rules as

for i in 0...100 {
    process(i, Rules)
}

You can also modify it to include more numbers, like

let Rules = [
    [3: "Fizz"],
    [5: "Buzz"],
    [7: "Baz"],
]

Summary

In conclusion, the rule based approach is an interesting one as it helps create flexibility and can adapt to changes as required. In some ways think if you have ever used commands on the command line, you can pipe the results of a command as the inputs for another. something like ls | wc -l which would return the number of lines that ls command prints.

In proper functional programming methodology, we would instead of passing parameters, we would pipe the function to another function. We could talk about this in detail in other subsequent articles.

Views All Time
Views All Time
1280
Views Today
Views Today
1
Posted in Algorithm, Tutorial.