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
-
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
-
IsPalindrome:
- Ignore case and spaces
- Consider empty string as palindrome
- Handle Unicode characters correctly
-
GradeToLetter:
- Use switch statement with range cases
- Return "Invalid" for grades outside 0-100
- Use fallthrough if necessary
-
FindPrimes:
- Implement Sieve of Eratosthenes algorithm
- Handle edge cases
- Use nested loops efficiently
-
SumMatrix:
- Use nested loops to traverse 2D slice
- Handle empty matrices
- Consider different row lengths
-
ReverseString:
- Work with Unicode characters
- Don't use built-in reverse functions
- Use for loop with range
-
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
- For Loop Versatility: Go's
forloop can be used as a while loop, infinite loop, or traditional for loop - Switch Flexibility: Switch statements in Go don't require explicit break and can switch on boolean conditions
- Unicode Awareness: Always use
runeslices when working with potentially non-ASCII strings - Algorithm Efficiency: The Sieve of Eratosthenes demonstrates how clever loop usage can optimize performance
- Short Statement Syntax: Use
if err := someFunc(); err != nilpattern for cleaner code
Additional Challenges
- Modify
FizzBuzzto accept custom divisors and words - Implement
IsPalindrometo handle sentences with punctuation - Add more complex grade calculations
- Optimize
FindPrimesfor very large values of n - Create a
TransposeMatrixfunction using nested loops - Implement an iterative version of binary search
- Create a function that finds the longest consecutive sequence in an array
Related Topics
- Control Flow - Main tutorial on control structures
- Functions - Function design and recursion
- Slices and Arrays - Working with collections
- String Processing - Advanced string operations