Algorithms: 1. FizzBuzz

Algorithms: 1. FizzBuzz

Mar 19, 2022
Programming, Golang, Algorithms

Introduction #

Go is a language I have only recently learnt, and so I decided, for practice, to write some of the standard algorithms and data structures in Go. None of these are particular new, all of them can be found on the web already, but the exercise for me was partly to remind myself of them, and partly to see what I learned along the way. The first one I decided to start off with was FizzBuzz.

FizzBuzz is traditionally a game used by teachers to teach children about division. Given a number, the children either call out the number, or “fizz” if the number is divisible by 3, “buzz” if the number is divisible by 5, and “fizzbuzz” if it’s divisible by both. It also used sometimes in computer programmer interviews. Writing a program which does this for the numbers 1 to 100 is trivial for most programmers, particularly if they do it in a language they are familiar with. If they can’t do that in a reasonable timeframe it gives some indication of their programming skills.

Initial Steps #

When writing a new programming you first need to figure out roughly what the flow is going to be. The first step seems obvious, have a main function that calls another function which does the actual figuring out whether to return the number, “fizz”, “buzz” or “fizzbuzz”.

flowchart LR A("main()") --> B("fizzbuzz()")

The second question was whether the user was going to have any input to the program. The answer to this was initially no.

The third question was how the fizzbuzz function would work? Would it take the entire array and calculate the result for the entire array, or would it just take a number and return the appropriate value? In the end I decide to go with the take an entire array option, partly because this is the way that things like sort work as well. To start with the array would be a fixed array of the numbers 1 to 100, and the type of the array would be []int.

The fourth question was what would the fizzbuzz function return? My initial reaction was just to return an array the same as input, but we can’t do that. The input array we have decided is an array of integers ([]int), but the result will contain not only numbers but also the strings “fizz”, “buzz” and “fizzbuzz”. So I decided to try returning an array of strings ([]string).

So now I could put together a skeleton for the program.

// fizzbuzz.go
package main

import (
    "fmt"
)

func fizzBuzz(array []int) (result []string) {

    // Logic to figure out what to return goes here

    return result
}

func main() {

    var size int = 100
    var array []int

    // Load the input array
    for i := 1; i <= size; i++ {
        array = append(array, i)
    }

    // Output
    fmt.Printf("Initial Array:  %s\n", array)
    fmt.Printf("Returned Array: %s\n", fizzBuzz(array))
}

If you save this file to fizzbuzz.go, you should be able to run this with go run fizzbuzz.go. The output should look like this:

Initial Array:  [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100]
Returned Array: []

Adding the logic #

OK. So far so good. Now to fill in the logic for the fizzbuzz function.

// fizzbuzz.go
package main

import (
    "fmt"
    "strconv"
)

func fizzBuzz(array []int) (result []string) {

    // Logic to figure out what to return goes here
    for i := 0; i < len(array); i++ {
        if array[i]%3 == 0 && array[i]%5 == 0 {
            result = append(result, "fizzbuzz")
        } else if array[i]%3 == 0 {
            result = append(result, "fizz")
        } else if array[i]%5 == 0 {
                result = append(result, "buzz")
        } else {
            result = append(result, strconv.Itoa(array[i]))
        }
    }

    return result
}

func main() {

    var size int = 100
    var array []int

    // Load the input array
    for i := 1; i <= size; i++ {
        array = append(array, i)
    }

    // Output
    fmt.Printf("Initial Array:  %s\n", array)
    fmt.Printf("Returned Array: %s\n", fizzBuzz(array))
}

Running this version gives us the following output:

Initial Array:  [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100]
Returned Array: [1 2 fizz 4 buzz fizz 7 8 fizz buzz 11 fizz 13 14 fizzbuzz 16 17 fizz 19 buzz fizz 22 23 fizz buzz 26 fizz 28 29 fizzbuzz 31 32 fizz 34 buzz fizz 37 38 fizz buzz 41 fizz 43 44 fizzbuzz 46 47 fizz 49 buzz fizz 52 53 fizz buzz 56 fizz 58 59 fizzbuzz 61 62 fizz 64 buzz fizz 67 68 fizz buzz 71 fizz 73 74 fizzbuzz 76 77 fizz 79 buzz fizz 82 83 fizz buzz 86 fizz 88 89 fizzbuzz 91 92 fizz 94 buzz fizz 97 98 fizz buzz]

So all is looking good. But what if you want to calculate the values up to a 1000?

Adding User Input #

Allowing user input is always problematic. There are various questions we need to ask ourselves if we are going to allow user input:

  1. We need to decide what we are going to allow the user to enter. In our case it was decided to allow the user to enter the start and end numbers for the array.
  2. Once we have decided what the user can enter we need to decide on the validation for those entries. We cannot guarantee that what the user enters will be correct, so we need to check that what they have entered makes sense and will not crash the program.
  3. We also need to check that even if what they have entered makes sense, is it allowed by the data types we are using? In our case the input array is an array of int, so the minimum and maximum values we can allow are as follows: On 32 bit systems -2147483648 to 2147483647, and on 64 bit systems -9223372036854775808 to 9223372036854775807.
  4. The final question once we know the above is how are we going to allow the user to input the numbers. In our case it is relatively simple. The program is a command line program so we can just the use the inbuilt flag module to allow the user to input the values. In graphical programs though we may need to think about what sort of widget makes sense for the values we are going to allow.
// fizzbuzz.go
package main

import (
    "flag"
    "fmt"
    "os"
    "strconv"
)

func fizzBuzz(array []int) (result []string) {

    // Logic to figure out what to return goes here
    for i := 0; i < len(array); i++ {
        if array[i]%3 == 0 && array[i]%5 == 0 {
            result = append(result, "fizzbuzz")
        } else if array[i]%3 == 0 {
            result = append(result, "fizz")
        } else if array[i]%5 == 0 {
                result = append(result, "buzz")
        } else {
            result = append(result, strconv.Itoa(array[i]))
        }
    }

    return result
}

func main() {

    var array []int
    var start int
    var end int

    // Define the flags and there default values
    flag.IntVar(&start, "start", 1, "The start element of the array")
    flag.IntVar(&end, "end", 100, "The end element of the array")

    // If start or end are less than or greater than int allows
    // the parse will fail and exit
    flag.Parse()

    if start > end {
            fmt.Println("The start number must be less than or equal to the end number")
            os.Exit(1)
    }

    // Initialize the input array
    for i := start; i <= end; i++ {
            array = append(array, i)
    }

    fmt.Printf("Initial Array:  %d\n", array)
    fmt.Printf("Returned Array: %s\n", fizzBuzz(array))
}

Testing #

So now if we do go build we can test the program.

  • Show the usage:
# Get the help
./fizzbuzz --help
Usage of ./fizzbuzz:
  -end int
        The end element of the array (default 100)
  -start int
        The start element of the array (default 1)
  • Try with some valid integers:
./fizzbuzz --start=-100 --end=0
Initial Array:  [-100 -99 -98 -97 -96 -95 -94 -93 -92 -91 -90 -89 -88 -87 -86 -85 -84 -83 -82 -81 -80 -79 -78 -77 -76 -75 -74 -73 -72 -71 -70 -69 -68 -67 -66 -65 -64 -63 -62 -61 -60 -59 -58 -57 -56 -55 -54 -53 -52 -51 -50 -49 -48 -47 -46 -45 -44 -43 -42 -41 -40 -39 -38 -37 -36 -35 -34 -33 -32 -31 -30 -29 -28 -27 -26 -25 -24 -23 -22 -21 -20 -19 -18 -17 -16 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0]
Returned Array: [buzz fizz -98 -97 fizz buzz -94 fizz -92 -91 fizzbuzz -89 -88 fizz -86 buzz fizz -83 -82 fizz buzz -79 fizz -77 -76 fizzbuzz -74 -73 fizz -71 buzz fizz -68 -67 fizz buzz -64 fizz -62 -61 fizzbuzz -59 -58 fizz -56 buzz fizz -53 -52 fizz buzz -49 fizz -47 -46 fizzbuzz -44 -43 fizz -41 buzz fizz -38 -37 fizz buzz -34 fizz -32 -31 fizzbuzz -29 -28 fizz -26 buzz fizz -23 -22 fizz buzz -19 fizz -17 -16 fizzbuzz -14 -13 fizz -11 buzz fizz -8 -7 fizz buzz -4 fizz -2 -1 fizzbuzz]
  • Make the start value and the end value the same:
./fizzbuzz --start=10 --end=10
Initial Array:  [10]
Returned Array: [buzz]
  • Set the end value less than the start value
./fizzbuzz --start=10 --end=0
The start number must be less than or equal to the end number
  • Try using floats instead of ints for the start and end values:
./fizzbuzz --start=1.0 --end=10.00
invalid value "1.0" for flag -start: parse error
Usage of ./fizzbuzz:
  -end int
        The end element of the array (default 100)
  -start int
        The start element of the array (default 1)
  • Try setting the end value to be greater than the size of int
./fizzbuzz --start=100 --end=9999999999999999999
invalid value "9999999999999999999" for flag -end: value out of range
Usage of ./fizzbuzz:
  -end int
        The end element of the array (default 100)
  -start int
        The start element of the array (default 1)

So now we have a user-driven command line application of fizzbuzz.