Control Flow Mastery

Exercise: Control Flow Mastery

Difficulty - Beginner

Learning Objectives

  • Master for loops and their various forms
  • Understand if-else statements and short statement syntax
  • Learn switch statement patterns and type switches
  • Practice break, continue, and goto statements
  • Implement common control flow patterns

Problem Statement

Create a ControlFlow package that implements various functions demonstrating Go's control flow mechanisms. You'll build functions that use loops, conditionals, and switch statements to solve practical problems.

Function Signatures

 1package controlflow
 2
 3// FizzBuzz returns FizzBuzz output for numbers 1 to n
 4func FizzBuzz(n int) []string
 5
 6// IsPalindrome checks if a string is a palindrome
 7func IsPalindrome(s string) bool
 8
 9// GradeToLetter converts a numeric grade to a letter grade
10// A: 90-100, B: 80-89, C: 70-79, D: 60-69, F: 0-59
11func GradeToLetter(grade int) string
12
13// FindPrimes returns all prime numbers up to n using Sieve of Eratosthenes
14func FindPrimes(n int) []int
15
16// SumMatrix returns the sum of all elements in a 2D matrix
17func SumMatrix(matrix [][]int) int
18
19// ReverseString reverses a string without using built-in reverse
20func ReverseString(s string) string
21
22// Factorial calculates n! using both iterative and recursive approaches
23func FactorialIterative(n int) int
24func FactorialRecursive(n int) int

Example Usage

 1package main
 2
 3import (
 4	"fmt"
 5	"controlflow"
 6)
 7
 8func main() {
 9	// FizzBuzz
10	result := controlflow.FizzBuzz(15)
11	fmt.Println(result)
12	// Output: [1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz]
13
14	// Palindrome check
15	fmt.Println(controlflow.IsPalindrome("A man a plan a canal Panama"))  // true
16	fmt.Println(controlflow.IsPalindrome("hello"))  // false
17
18	// Grade conversion
19	fmt.Println(controlflow.GradeToLetter(95))  // A
20	fmt.Println(controlflow.GradeToLetter(73))  // C
21
22	// Find primes
23	primes := controlflow.FindPrimes(20)
24	fmt.Println(primes)  // [2 3 5 7 11 13 17 19]
25
26	// Sum matrix
27	matrix := [][]int{
28		{1, 2, 3},
29		{4, 5, 6},
30		{7, 8, 9},
31	}
32	fmt.Println(controlflow.SumMatrix(matrix))  // 45
33
34	// Reverse string
35	fmt.Println(controlflow.ReverseString("Hello"))  // olleH
36
37	// Factorial
38	fmt.Println(controlflow.FactorialIterative(5))   // 120
39	fmt.Println(controlflow.FactorialRecursive(5))   // 120
40}

Requirements

  1. FizzBuzz:

    • Return "Fizz" for multiples of 3
    • Return "Buzz" for multiples of 5
    • Return "FizzBuzz" for multiples of both
    • Return the number as a string otherwise
  2. IsPalindrome:

    • Ignore case and spaces
    • Consider empty string as palindrome
    • Handle Unicode characters correctly
  3. GradeToLetter:

    • Use switch statement with range cases
    • Return "Invalid" for grades outside 0-100
    • Use fallthrough if necessary
  4. FindPrimes:

    • Implement Sieve of Eratosthenes algorithm
    • Handle edge cases
    • Use nested loops efficiently
  5. SumMatrix:

    • Use nested loops to traverse 2D slice
    • Handle empty matrices
    • Consider different row lengths
  6. ReverseString:

    • Work with Unicode characters
    • Don't use built-in reverse functions
    • Use for loop with range
  7. Factorial:

    • Iterative version uses for loop
    • Recursive version uses function calls
    • Handle 0! = 1
    • Return -1 for negative inputs

Test Cases

  1package controlflow_test
  2
  3import (
  4	"reflect"
  5	"testing"
  6	"controlflow"
  7)
  8
  9func TestFizzBuzz(t *testing.T) {
 10	result := controlflow.FizzBuzz(15)
 11	expected := []string{"1", "2", "Fizz", "4", "Buzz", "Fizz", "7", "8",
 12		"Fizz", "Buzz", "11", "Fizz", "13", "14", "FizzBuzz"}
 13
 14	if !reflect.DeepEqual(result, expected) {
 15		t.Errorf("FizzBuzz(15) = %v, want %v", result, expected)
 16	}
 17}
 18
 19func TestIsPalindrome(t *testing.T) {
 20	tests := []struct {
 21		input string
 22		want  bool
 23	}{
 24		{"A man a plan a canal Panama", true},
 25		{"racecar", true},
 26		{"hello", false},
 27		{"", true},
 28		{"a", true},
 29	}
 30
 31	for _, tt := range tests {
 32		got := controlflow.IsPalindrome(tt.input)
 33		if got != tt.want {
 34			t.Errorf("IsPalindrome(%q) = %v, want %v", tt.input, got, tt.want)
 35		}
 36	}
 37}
 38
 39func TestGradeToLetter(t *testing.T) {
 40	tests := []struct {
 41		grade int
 42		want  string
 43	}{
 44		{95, "A"},
 45		{85, "B"},
 46		{75, "C"},
 47		{65, "D"},
 48		{55, "F"},
 49		{101, "Invalid"},
 50		{-1, "Invalid"},
 51	}
 52
 53	for _, tt := range tests {
 54		got := controlflow.GradeToLetter(tt.grade)
 55		if got != tt.want {
 56			t.Errorf("GradeToLetter(%d) = %q, want %q", tt.grade, got, tt.want)
 57		}
 58	}
 59}
 60
 61func TestFindPrimes(t *testing.T) {
 62	result := controlflow.FindPrimes(20)
 63	expected := []int{2, 3, 5, 7, 11, 13, 17, 19}
 64
 65	if !reflect.DeepEqual(result, expected) {
 66		t.Errorf("FindPrimes(20) = %v, want %v", result, expected)
 67	}
 68}
 69
 70func TestSumMatrix(t *testing.T) {
 71	matrix := [][]int{
 72		{1, 2, 3},
 73		{4, 5, 6},
 74		{7, 8, 9},
 75	}
 76
 77	result := controlflow.SumMatrix(matrix)
 78	expected := 45
 79
 80	if result != expected {
 81		t.Errorf("SumMatrix = %d, want %d", result, expected)
 82	}
 83}
 84
 85func TestReverseString(t *testing.T) {
 86	tests := []struct {
 87		input string
 88		want  string
 89	}{
 90		{"Hello", "olleH"},
 91		{"", ""},
 92		{"a", "a"},
 93		{"Hello, 世界", "界世 ,olleH"},
 94	}
 95
 96	for _, tt := range tests {
 97		got := controlflow.ReverseString(tt.input)
 98		if got != tt.want {
 99			t.Errorf("ReverseString(%q) = %q, want %q", tt.input, got, tt.want)
100		}
101	}
102}
103
104func TestFactorial(t *testing.T) {
105	tests := []struct {
106		input int
107		want  int
108	}{
109		{0, 1},
110		{1, 1},
111		{5, 120},
112		{10, 3628800},
113		{-1, -1},
114	}
115
116	for _, tt := range tests {
117		gotIter := controlflow.FactorialIterative(tt.input)
118		if gotIter != tt.want {
119			t.Errorf("FactorialIterative(%d) = %d, want %d", tt.input, gotIter, tt.want)
120		}
121
122		gotRec := controlflow.FactorialRecursive(tt.input)
123		if gotRec != tt.want {
124			t.Errorf("FactorialRecursive(%d) = %d, want %d", tt.input, gotRec, tt.want)
125		}
126	}
127}

Hints

Hint 1: FizzBuzz Logic

Use modulo operator to check divisibility. Check for divisibility by 15 first, then 3, then 5.

 1for i := 1; i <= n; i++ {
 2    if i%15 == 0 {
 3        // FizzBuzz
 4    } else if i%3 == 0 {
 5        // Fizz
 6    } else if i%5 == 0 {
 7        // Buzz
 8    } else {
 9        // number
10    }
11}
Hint 2: Palindrome Check

Convert to lowercase, remove spaces, then compare characters from both ends moving toward the center.

 1// Clean string first
 2cleaned := ""
 3for _, r := range strings.ToLower(s) {
 4    if r != ' ' {
 5        cleaned += string(r)
 6    }
 7}
 8
 9// Check palindrome
10for i := 0; i < len(cleaned)/2; i++ {
11    if cleaned[i] != cleaned[len(cleaned)-1-i] {
12        return false
13    }
14}
15return true
Hint 3: Switch with Ranges

Use switch with boolean true and cases with conditions:

1switch {
2case grade >= 90 && grade <= 100:
3    return "A"
4case grade >= 80:
5    return "B"
6// ... etc
7}
Hint 4: Sieve of Eratosthenes

Create a boolean array to mark composites, then iterate marking multiples:

 1isPrime := make([]bool, n+1)
 2for i := 2; i <= n; i++ {
 3    isPrime[i] = true
 4}
 5
 6for i := 2; i*i <= n; i++ {
 7    if isPrime[i] {
 8        for j := i * i; j <= n; j += i {
 9            isPrime[j] = false
10        }
11    }
12}

Solution

Click to see the complete solution
  1package controlflow
  2
  3import (
  4	"fmt"
  5	"strings"
  6)
  7
  8// FizzBuzz returns FizzBuzz output for numbers 1 to n
  9func FizzBuzz(n int) []string {
 10	result := make([]string, n)
 11
 12	for i := 1; i <= n; i++ {
 13		switch {
 14		case i%15 == 0:
 15			result[i-1] = "FizzBuzz"
 16		case i%3 == 0:
 17			result[i-1] = "Fizz"
 18		case i%5 == 0:
 19			result[i-1] = "Buzz"
 20		default:
 21			result[i-1] = fmt.Sprintf("%d", i)
 22		}
 23	}
 24
 25	return result
 26}
 27
 28// IsPalindrome checks if a string is a palindrome
 29func IsPalindrome(s string) bool {
 30	// Clean string: lowercase and remove spaces
 31	cleaned := ""
 32	for _, r := range strings.ToLower(s) {
 33		if r != ' ' {
 34			cleaned += string(r)
 35		}
 36	}
 37
 38	// Check if palindrome
 39	runes := []rune(cleaned)
 40	for i := 0; i < len(runes)/2; i++ {
 41		if runes[i] != runes[len(runes)-1-i] {
 42			return false
 43		}
 44	}
 45
 46	return true
 47}
 48
 49// GradeToLetter converts numeric grade to letter grade
 50func GradeToLetter(grade int) string {
 51	switch {
 52	case grade < 0 || grade > 100:
 53		return "Invalid"
 54	case grade >= 90:
 55		return "A"
 56	case grade >= 80:
 57		return "B"
 58	case grade >= 70:
 59		return "C"
 60	case grade >= 60:
 61		return "D"
 62	default:
 63		return "F"
 64	}
 65}
 66
 67// FindPrimes returns all prime numbers up to n using Sieve of Eratosthenes
 68func FindPrimes(n int) []int {
 69	if n < 2 {
 70		return []int{}
 71	}
 72
 73	// Create boolean slice to mark primes
 74	isPrime := make([]bool, n+1)
 75	for i := 2; i <= n; i++ {
 76		isPrime[i] = true
 77	}
 78
 79	// Sieve algorithm
 80	for i := 2; i*i <= n; i++ {
 81		if isPrime[i] {
 82			// Mark all multiples as composite
 83			for j := i * i; j <= n; j += i {
 84				isPrime[j] = false
 85			}
 86		}
 87	}
 88
 89	// Collect primes
 90	primes := []int{}
 91	for i := 2; i <= n; i++ {
 92		if isPrime[i] {
 93			primes = append(primes, i)
 94		}
 95	}
 96
 97	return primes
 98}
 99
100// SumMatrix returns the sum of all elements in a 2D matrix
101func SumMatrix(matrix [][]int) int {
102	sum := 0
103
104	for i := 0; i < len(matrix); i++ {
105		for j := 0; j < len(matrix[i]); j++ {
106			sum += matrix[i][j]
107		}
108	}
109
110	return sum
111}
112
113// ReverseString reverses a string
114func ReverseString(s string) string {
115	// Convert to rune slice for Unicode support
116	runes := []rune(s)
117
118	// Reverse in place
119	for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
120		runes[i], runes[j] = runes[j], runes[i]
121	}
122
123	return string(runes)
124}
125
126// FactorialIterative calculates factorial iteratively
127func FactorialIterative(n int) int {
128	if n < 0 {
129		return -1
130	}
131
132	if n == 0 {
133		return 1
134	}
135
136	result := 1
137	for i := 1; i <= n; i++ {
138		result *= i
139	}
140
141	return result
142}
143
144// FactorialRecursive calculates factorial recursively
145func FactorialRecursive(n int) int {
146	if n < 0 {
147		return -1
148	}
149
150	if n == 0 || n == 1 {
151		return 1
152	}
153
154	return n * FactorialRecursive(n-1)
155}

Key Takeaways

  1. For Loop Versatility: Go's for loop can be used as a while loop, infinite loop, or traditional for loop
  2. Switch Flexibility: Switch statements in Go don't require explicit break and can switch on boolean conditions
  3. Unicode Awareness: Always use rune slices when working with potentially non-ASCII strings
  4. Algorithm Efficiency: The Sieve of Eratosthenes demonstrates how clever loop usage can optimize performance
  5. Short Statement Syntax: Use if err := someFunc(); err != nil pattern for cleaner code

Additional Challenges

  1. Modify FizzBuzz to accept custom divisors and words
  2. Implement IsPalindrome to handle sentences with punctuation
  3. Add more complex grade calculations
  4. Optimize FindPrimes for very large values of n
  5. Create a TransposeMatrix function using nested loops
  6. Implement an iterative version of binary search
  7. Create a function that finds the longest consecutive sequence in an array