August 29, 2015 · golang concurrency tech interviews

FizzBuzz in Golang

FizzBuzz goes like so:

Write a program that prints the numbers 1 to 100.

For multiples of three, print "Fizz".

For multiples of five, print "Buzz".

For multiples of both three and five, print "FizzBuzz".

In a hurry? Fast-forward to the Wrapup for working code.

Qualifying the problem

Let's start by breaking things down into pseudo-code using the FizzBuzz problem statement:

Write a program that prints the numbers 1 to 100.

for i = 1; i <= 100; i++
    print i

1
2
3
4
...

For multiples of three, print "Fizz".

for i = 1; i <= 100; i++
    if i % 3 == 0
        print "Fizz"
    else
        print i

1
2
Fizz
4
5
Fizz
7
...

For multiples of five, print "Buzz".

for i = 1; i <= 100; i++ 
    if i % 3 == 0
        print "Fizz"
    else if i % 5 == 0
        print "Buzz"
    else
        print i

1
2
Fizz
4
Buzz
Fizz
7
...

For multiples of both three and five, print "FizzBuzz".

for i = 1; i <= 100; i++
    if i % 15 == 0
        print "FizzBuzz"
    else if i % 3 == 0
        print "Fizz"
    else if i % 5 == 0
        print "Buzz"
    else
        print i

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

Solving it

Now that we have some pseudo-code, let's translate it to into Go:

package main

import "fmt"

func main() {
    for i := 1; i <= 100; i++ {
        if i%15 == 0 {
            fmt.Println("FizzBuzz")
        } else if i%3 == 0 {
            fmt.Println("Fizz")
        } else if i%5 == 0 {
            fmt.Println("Buzz")
        } else {
            fmt.Println(i)
        }
    }
}

When we run our program, we can see that the output is correct:

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

Congrats - unicorns have been saved and the world is well.

Run this code

An Optimization

The above solution worked, but it's not perfect:

  • Our first conditional, if i%15 == 0, is ran every time, even though it's only true every 15 times.
  • We've duplicated the strings Fizz and Buzz between the calls to fmt.Println.

With a little fancy business, we can remedy both these issues:

package main

import "fmt"

func main() {
	for i := 1; i <= 100; i++ {
		result := ""
		if i%3 == 0 { result += "Fizz" }
		if i%5 == 0 { result += "Buzz" }
		if result != "" {
			fmt.Println(result)
			continue
		}
		fmt.Println(i)	
	}
}

Now we don't need to calculate i%15 == 0 at all, and we never repeat ourselves. Win.

Run this code

Writing tests

Because we are all good programmers who verify our programs, let's move our fizzbuzz functionality to its own testable function. For bonus points, we're going to add an amount param to the function that determines how many numbers to output:

func FizzBuzz(amount int) string {
	results := ""
	for i := 1; i <= amount; i++ {
		result := ""
		if i%3 == 0 { result += "Fizz" }
		if i%5 == 0 { result += "Buzz" }
		if result != "" {
			results += result + "\n"
			continue
		}
		results += fmt.Sprintf("%d\n", i)
	}
	return results
}

Now we could call the function from main, or from a test like so:

func TestFizzBuzz(t *testing.T) {
	got := FizzBuzz(15)
	want := 
`1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
`
	if got != want {
		t.Errorf("FizzBuzz(15) \n got: \n%v \n want: \n%v", got, want)
	} 
}

Run this code

Using a Goroutine

As a final improvement, we're going to run our FizzBuzz function in a goroutine, streaming it's results through a channel that the main function will read from to progressivly output the results:

package main

import "fmt"

func main() {
	for out := range FizzBuzz(100) {
		fmt.Println(out)
	}
}

func FizzBuzz(amount int) <-chan string {

	out := make(chan string, amount)

	go func() {
		for i := 1; i <= amount; i++ {
			result := ""
			if i%3 == 0 { result += "Fizz" }
			if i%5 == 0 { result += "Buzz" }
			if result == "" { result = fmt.Sprintf("%v", i) }
			out <- result
		}
		close(out)
	}()

	return out
}

Run this code

Wrapup

Here are 4 runnable Go programs for solving FizzBuzz: