Lompat ke konten
Beranda ยป Control Flow di Go: If, Switch, dan Loops – Panduan Lengkap Struktur Kontrol

Control Flow di Go: If, Switch, dan Loops – Panduan Lengkap Struktur Kontrol

Control Flow in Go Illustration

Menguasai Struktur Kontrol Program Go yang Unik

Setelah memahami sintaks dasar Go seperti variables dan data types, langkah selanjutnya adalah mempelajari control flow di Go. Struktur kontrol menentukan bagaimana program Anda mengambil keputusan dan menjalankan perulangan.

Control flow Go memiliki keunikan tersendiri dibanding bahasa pemrograman lain. Artikel ini akan membahas secara mendalam:

  • If statements dengan fitur unik Go
  • Switch statements yang powerful dan fleksibel
  • For loops sebagai satu-satunya loop di Go
  • Break, continue, dan goto untuk kontrol advanced
  • Best practices

Mengapa Control Flow Go Berbeda dan Lebih Baik?

Go control flow dirancang dengan prinsip simplicity dan consistency:

  • Tidak ada while atau do-while – hanya for loop
  • Switch tanpa fallthrough default – lebih aman
  • If dengan initialization statement – lebih efisien
  • Type switch untuk interface handling
  • Label dan goto untuk kontrol kompleks

Mari kita eksplorasi setiap komponen ini secara detail.

If Statements di Go: Simple tapi Powerful

1. Basic If Statement

Sintaks dasar if di Go:

if condition {
    // code to execute
}

Contoh implementasi:

package main

import "fmt"

func main() {
    age := 18
    score := 85
    
    // Basic if
    if age >= 18 {
        fmt.Println("Anda sudah dewasa")
    }
    
    // If dengan multiple conditions
    if age >= 18 && score >= 80 {
        fmt.Println("Memenuhi syarat beasiswa")
    }
    
    // If dengan logical operators
    if age < 17 || score < 60 {
        fmt.Println("Belum memenuhi syarat")
    } else {
        fmt.Println("Selamat, Anda lolos!")
    }
}

2. If-Else dan Else-If Chains

func gradeSystem() {
    score := 85
    
    // If-else if-else chain
    if score >= 90 {
        fmt.Println("Grade: A")
    } else if score >= 80 {
        fmt.Println("Grade: B")
    } else if score >= 70 {
        fmt.Println("Grade: C")
    } else if score >= 60 {
        fmt.Println("Grade: D")
    } else {
        fmt.Println("Grade: F")
    }
    
    // Nested if statements
    if score >= 60 {
        if score >= 80 {
            fmt.Println("Excellent performance!")
        } else {
            fmt.Println("Good job!")
        }
    } else {
        fmt.Println("Need improvement")
    }
}

3. If dengan Initialization Statement (Fitur Unik Go)

Ini adalah fitur yang membuat Go unik:

package main

import (
    "fmt"
    "math/rand"
    "time"
)

func main() {
    // If dengan initialization - variable hanya available dalam if block
    if num := rand.Intn(100); num > 50 {
        fmt.Printf("Number %d is greater than 50\n", num)
    } else {
        fmt.Printf("Number %d is less than or equal to 50\n", num)
    }
    // 'num' tidak bisa diakses di luar if block
    
    // Practical example: Error handling
    if err := someFunction(); err != nil {
        fmt.Printf("Error occurred: %v\n", err)
        return
    }
    
    // File operations example
    if data, err := readFile("config.txt"); err != nil {
        fmt.Printf("Failed to read file: %v\n", err)
    } else {
        fmt.Printf("File content: %s\n", data)
    }
}

func someFunction() error {
    // Simulate function that might return error
    if rand.Intn(2) == 0 {
        return fmt.Errorf("random error occurred")
    }
    return nil
}

func readFile(filename string) (string, error) {
    // Simulate file reading
    return "sample content", nil
}

4. Comparison Operators dalam If

func comparisonExamples() {
    a, b := 10, 20
    name1, name2 := "Alice", "Bob"
    
    // Numeric comparisons
    fmt.Println("a == b:", a == b)  // false
    fmt.Println("a != b:", a != b)  // true
    fmt.Println("a < b:", a < b)    // true
    fmt.Println("a <= b:", a <= b)  // true
    fmt.Println("a > b:", a > b)    // false
    fmt.Println("a >= b:", a >= b)  // false
    
    // String comparisons
    if name1 < name2 {  // Lexicographic comparison
        fmt.Printf("%s comes before %s\n", name1, name2)
    }
    
    // Boolean logic
    isActive := true
    isVerified := false
    
    if isActive && !isVerified {
        fmt.Println("Account is active but not verified")
    }
    
    if isActive || isVerified {
        fmt.Println("Account has some level of access")
    }
}

Switch Statements di Go: Powerful dan Fleksible

1. Basic Switch Statement

Switch di Go tidak memerlukan break statement:

package main

import (
    "fmt"
    "time"
)

func main() {
    day := time.Now().Weekday()
    
    // Basic switch
    switch day {
    case time.Monday:
        fmt.Println("Hari Senin - Semangat kerja!")
    case time.Tuesday:
        fmt.Println("Hari Selasa - Tetap semangat!")
    case time.Wednesday:
        fmt.Println("Hari Rabu - Pertengahan minggu!")
    case time.Thursday:
        fmt.Println("Hari Kamis - Hampir weekend!")
    case time.Friday:
        fmt.Println("Hari Jumat - TGIF!")
    case time.Saturday, time.Sunday:
        fmt.Println("Weekend - Waktu istirahat!")
    default:
        fmt.Println("Hari tidak dikenal")
    }
}

2. Switch dengan Multiple Values

func switchMultipleValues() {
    grade := 'B'
    
    switch grade {
    case 'A', 'a':
        fmt.Println("Excellent!")
    case 'B', 'b':
        fmt.Println("Good job!")
    case 'C', 'c':
        fmt.Println("Average")
    case 'D', 'd':
        fmt.Println("Below average")
    case 'F', 'f':
        fmt.Println("Failed")
    default:
        fmt.Println("Invalid grade")
    }
    
    // Switch dengan range conditions
    score := 85
    
    switch {
    case score >= 90:
        fmt.Println("Grade A")
    case score >= 80:
        fmt.Println("Grade B")
    case score >= 70:
        fmt.Println("Grade C")
    case score >= 60:
        fmt.Println("Grade D")
    default:
        fmt.Println("Grade F")
    }
}

3. Switch dengan Initialization Statement

package main

import (
    "fmt"
    "math/rand"
    "time"
)

func main() {
    rand.Seed(time.Now().UnixNano())
    
    // Switch dengan initialization
    switch num := rand.Intn(100); {
    case num < 25:
        fmt.Printf("Number %d is in first quartile\n", num)
    case num < 50:
        fmt.Printf("Number %d is in second quartile\n", num)
    case num < 75:
        fmt.Printf("Number %d is in third quartile\n", num)
    default:
        fmt.Printf("Number %d is in fourth quartile\n", num)
    }
    
    // Switch untuk error handling
    switch err := performOperation(); err {
    case nil:
        fmt.Println("Operation successful")
    case ErrInvalidInput:
        fmt.Println("Invalid input provided")
    case ErrConnectionFailed:
        fmt.Println("Connection failed")
    default:
        fmt.Printf("Unknown error: %v\n", err)
    }
}

// Custom error types
var (
    ErrInvalidInput     = fmt.Errorf("invalid input")
    ErrConnectionFailed = fmt.Errorf("connection failed")
)

func performOperation() error {
    // Simulate random operation result
    switch rand.Intn(4) {
    case 0:
        return nil
    case 1:
        return ErrInvalidInput
    case 2:
        return ErrConnectionFailed
    default:
        return fmt.Errorf("unexpected error")
    }
}

4. Type Switch (Fitur Advanced)

package main

import "fmt"

func main() {
    var items []interface{} = []interface{}{
        42,
        "hello",
        3.14,
        true,
        []int{1, 2, 3},
        map[string]int{"key": 100},
    }
    
    for i, item := range items {
        switch v := item.(type) {
        case int:
            fmt.Printf("Item %d: Integer %d\n", i, v)
        case string:
            fmt.Printf("Item %d: String '%s' (length: %d)\n", i, v, len(v))
        case float64:
            fmt.Printf("Item %d: Float %.2f\n", i, v)
        case bool:
            fmt.Printf("Item %d: Boolean %t\n", i, v)
        case []int:
            fmt.Printf("Item %d: Integer slice %v\n", i, v)
        case map[string]int:
            fmt.Printf("Item %d: String-Int map %v\n", i, v)
        default:
            fmt.Printf("Item %d: Unknown type %T\n", i, v)
        }
    }
}

5. Fallthrough dalam Switch

func fallthroughExample() {
    grade := 'B'
    
    switch grade {
    case 'A':
        fmt.Println("Excellent!")
        fallthrough  // Lanjut ke case berikutnya
    case 'B':
        fmt.Println("Above average")
        fallthrough
    case 'C':
        fmt.Println("Passed the exam")
    case 'D':
        fmt.Println("Need improvement")
    default:
        fmt.Println("Failed")
    }
    
    // Output untuk grade 'B':
    // Above average
    // Passed the exam
}

For Loops di Go: Satu Loop untuk Semua Kebutuhan

1. Basic For Loop (C-style)

package main

import "fmt"

func main() {
    // Traditional for loop
    fmt.Println("Counting up:")
    for i := 1; i <= 5; i++ {
        fmt.Printf("Count: %d\n", i)
    }
    
    // Counting down
    fmt.Println("\nCountdown:")
    for i := 5; i >= 1; i-- {
        fmt.Printf("T-minus %d\n", i)
    }
    
    // Custom increment
    fmt.Println("\nEven numbers:")
    for i := 0; i <= 10; i += 2 {
        fmt.Printf("Even: %d\n", i)
    }
    
    // Multiple variables
    fmt.Println("\nFibonacci sequence:")
    for a, b := 0, 1; a < 100; a, b = b, a+b {
        fmt.Printf("Fibonacci: %d\n", a)
    }
}

2. While-style For Loop

func whileStyleLoop() {
    // Go tidak punya while, tapi bisa simulate dengan for
    count := 0
    
    for count < 5 {
        fmt.Printf("While-style count: %d\n", count)
        count++
    }
    
    // Infinite loop dengan break condition
    counter := 0
    for {  // Infinite loop
        if counter >= 3 {
            break
        }
        fmt.Printf("Infinite loop iteration: %d\n", counter)
        counter++
    }
}

3. Range-based For Loop

func rangeLoopExamples() {
    // Array/Slice iteration
    numbers := []int{10, 20, 30, 40, 50}
    
    // With index and value
    fmt.Println("Array with index and value:")
    for i, num := range numbers {
        fmt.Printf("Index %d: %d\n", i, num)
    }
    
    // Only value
    fmt.Println("\nOnly values:")
    for _, num := range numbers {
        fmt.Printf("Value: %d\n", num)
    }
    
    // Only index
    fmt.Println("\nOnly indices:")
    for i := range numbers {
        fmt.Printf("Index: %d\n", i)
    }
    
    // String iteration (runes)
    text := "Hello"
    fmt.Println("\nString iteration:")
    for i, char := range text {
        fmt.Printf("Position %d: %c (Unicode: %d)\n", i, char, char)
    }
    
    // Map iteration
    scores := map[string]int{
        "Alice": 95,
        "Bob":   87,
        "Carol": 92,
    }
    
    fmt.Println("\nMap iteration:")
    for name, score := range scores {
        fmt.Printf("%s: %d\n", name, score)
    }
    
    // Channel iteration (akan dibahas di artikel terpisah)
    ch := make(chan int, 3)
    ch <- 1
    ch <- 2
    ch <- 3
    close(ch)
    
    fmt.Println("\nChannel iteration:")
    for value := range ch {
        fmt.Printf("Channel value: %d\n", value)
    }
}

4. Nested Loops

func nestedLoopExamples() {
    // Multiplication table
    fmt.Println("Multiplication Table:")
    for i := 1; i <= 5; i++ {
        for j := 1; j <= 5; j++ {
            fmt.Printf("%2d ", i*j)
        }
        fmt.Println()
    }
    
    // Matrix operations
    matrix := [][]int{
        {1, 2, 3},
        {4, 5, 6},
        {7, 8, 9},
    }
    
    fmt.Println("\nMatrix:")
    for i, row := range matrix {
        for j, value := range row {
            fmt.Printf("matrix[%d][%d] = %d\n", i, j, value)
        }
    }
    
    // Pattern printing
    fmt.Println("\nStar pattern:")
    for i := 1; i <= 5; i++ {
        for j := 1; j <= i; j++ {
            fmt.Print("* ")
        }
        fmt.Println()
    }
}

Break, Continue, dan Goto Statements

1. Break Statement

func breakExamples() {
    // Break dalam loop
    fmt.Println("Break example:")
    for i := 1; i <= 10; i++ {
        if i == 6 {
            fmt.Println("Breaking at 6")
            break
        }
        fmt.Printf("Number: %d\n", i)
    }
    
    // Break dengan label (nested loops)
    fmt.Println("\nBreak with label:")
outer:
    for i := 1; i <= 3; i++ {
        for j := 1; j <= 3; j++ {
            if i == 2 && j == 2 {
                fmt.Println("Breaking outer loop")
                break outer
            }
            fmt.Printf("i=%d, j=%d\n", i, j)
        }
    }
    
    // Break dalam switch (otomatis, tapi bisa explicit)
    value := 2
    switch value {
    case 1:
        fmt.Println("One")
    case 2:
        fmt.Println("Two")
        // break  // Implicit break, tidak perlu ditulis
    case 3:
        fmt.Println("Three")
    }
}

2. Continue Statement

func continueExamples() {
    // Skip even numbers
    fmt.Println("Odd numbers only:")
    for i := 1; i <= 10; i++ {
        if i%2 == 0 {
            continue  // Skip even numbers
        }
        fmt.Printf("Odd: %d\n", i)
    }
    
    // Continue dengan label
    fmt.Println("\nContinue with label:")
outer:
    for i := 1; i <= 3; i++ {
        for j := 1; j <= 3; j++ {
            if j == 2 {
                fmt.Printf("Skipping i=%d, j=%d\n", i, j)
                continue outer
            }
            fmt.Printf("Processing i=%d, j=%d\n", i, j)
        }
    }
    
    // Practical example: filtering data
    numbers := []int{1, -2, 3, -4, 5, -6, 7, -8, 9, -10}
    fmt.Println("\nPositive numbers only:")
    for _, num := range numbers {
        if num < 0 {
            continue
        }
        fmt.Printf("Positive: %d\n", num)
    }
}

3. Goto Statement (Use with Caution)

func gotoExample() {
    i := 0
    
    // Simple goto
start:
    fmt.Printf("Count: %d\n", i)
    i++
    
    if i < 3 {
        goto start
    }
    
    fmt.Println("Done with goto")
    
    // Goto untuk error handling (lebih baik pakai return)
    if err := checkCondition(); err != nil {
        goto errorHandler
    }
    
    fmt.Println("Normal execution")
    goto end
    
errorHandler:
    fmt.Println("Error occurred, cleaning up...")
    
end:
    fmt.Println("Program ends")
}

func checkCondition() error {
    // Simulate some condition
    return nil
}

Practical Examples: Real-World Control Flow

1. User Input Validation

package main

import (
    "fmt"
    "strconv"
    "strings"
)

func validateUserInput() {
    inputs := []string{"25", "abc", "0", "-5", "150"}
    
    for i, input := range inputs {
        fmt.Printf("\nValidating input %d: '%s'\n", i+1, input)
        
        // Clean input
        cleaned := strings.TrimSpace(input)
        if cleaned == "" {
            fmt.Println("Empty input")
            continue
        }
        
        // Convert to integer
        if age, err := strconv.Atoi(cleaned); err != nil {
            fmt.Printf("Invalid number: %v\n", err)
            continue
        } else {
            // Validate age range
            switch {
            case age < 0:
                fmt.Println("Age cannot be negative")
            case age == 0:
                fmt.Println("Age cannot be zero")
            case age > 120:
                fmt.Println("Age seems unrealistic")
            case age < 18:
                fmt.Printf("Minor: %d years old\n", age)
            case age >= 18 && age < 65:
                fmt.Printf("Adult: %d years old\n", age)
            default:
                fmt.Printf("Senior: %d years old\n", age)
            }
        }
    }
}

func main() {
    validateUserInput()
}

2. Menu System

func menuSystem() {
    for {
        fmt.Println("\n=== MAIN MENU ===")
        fmt.Println("1. View Profile")
        fmt.Println("2. Update Settings")
        fmt.Println("3. Check Balance")
        fmt.Println("4. Transfer Money")
        fmt.Println("5. Exit")
        fmt.Print("Choose option (1-5): ")
        
        var choice int
        fmt.Scanf("%d", &choice)
        
        switch choice {
        case 1:
            fmt.Println("Viewing profile...")
            viewProfile()
        case 2:
            fmt.Println("Updating settings...")
            updateSettings()
        case 3:
            fmt.Println("Checking balance...")
            checkBalance()
        case 4:
            fmt.Println("Transfer money...")
            if !transferMoney() {
                fmt.Println("Transfer cancelled")
            }
        case 5:
            fmt.Println("Goodbye!")
            return
        default:
            fmt.Println("Invalid option. Please choose 1-5")
            continue
        }
        
        // Wait for user input before showing menu again
        fmt.Println("\nPress Enter to continue...")
        fmt.Scanln()
    }
}

func viewProfile() {
    fmt.Println("Name: John Doe")
    fmt.Println("Email: john@example.com")
    fmt.Println("Member since: 2023")
}

func updateSettings() {
    settings := []string{"Language", "Notifications", "Privacy", "Security"}
    
    for i, setting := range settings {
        fmt.Printf("%d. %s\n", i+1, setting)
    }
}

func checkBalance() {
    fmt.Println("Current balance: $1,250.00")
    fmt.Println("Available balance: $1,200.00")
}

func transferMoney() bool {
    fmt.Print("Enter amount: $")
    var amount float64
    fmt.Scanf("%f", &amount)
    
    if amount <= 0 {
        fmt.Println("Invalid amount")
        return false
    }
    
    if amount > 1200 {
        fmt.Println("Insufficient funds")
        return false
    }
    
    fmt.Printf("Successfully transferred $%.2f\n", amount)
    return true
}

3. Data Processing Pipeline

func dataProcessingPipeline() {
    // Simulate raw data
    rawData := []map[string]interface{}{
        {"name": "Alice", "age": 25, "score": 95, "active": true},
        {"name": "", "age": 30, "score": 87, "active": true},
        {"name": "Bob", "age": -5, "score": 92, "active": false},
        {"name": "Carol", "age": 22, "score": 78, "active": true},
        {"name": "Dave", "age": 35, "score": 0, "active": true},
    }
    
    fmt.Println("=== DATA PROCESSING PIPELINE ===")
    
    validRecords := 0
    totalScore := 0
    activeUsers := 0
    
    for i, record := range rawData {
        fmt.Printf("\nProcessing record %d: %+v\n", i+1, record)
        
        // Validation pipeline
        name, nameOk := record["name"].(string)
        age, ageOk := record["age"].(int)
        score, scoreOk := record["score"].(int)
        active, activeOk := record["active"].(bool)
        
        // Type validation
        if !nameOk || !ageOk || !scoreOk || !activeOk {
            fmt.Println("Invalid data types")
            continue
        }
        
        // Business logic validation
        switch {
        case name == "":
            fmt.Println("Name is required")
            continue
        case age < 0 || age > 100:
            fmt.Println("Invalid age range")
            continue
        case score < 0 || score > 100:
            fmt.Println("Invalid score range")
            continue
        }
        
        // Process valid record
        fmt.Println("Record validated")
        validRecords++
        totalScore += score
        
        if active {
            activeUsers++
            
            // Additional processing for active users
            switch {
            case score >= 90:
                fmt.Printf("%s is a top performer!\n", name)
            case score >= 80:
                fmt.Printf("%s is doing great!\n", name)
            default:
                fmt.Printf("%s is doing well!\n", name)
            }
        }
    }
    
    // Summary
    fmt.Println("\n=== PROCESSING SUMMARY ===")
    fmt.Printf("Total records: %d\n", len(rawData))
    fmt.Printf("Valid records: %d\n", validRecords)
    fmt.Printf("Active users: %d\n", activeUsers)
    
    if validRecords > 0 {
        avgScore := float64(totalScore) / float64(validRecords)
        fmt.Printf("Average score: %.2f\n", avgScore)
    }
}

Best Practices untuk Control Flow

1. Readability dan Maintainability

// Good: Clear and readable
func processOrder(order Order) error {
    if order.ID == "" {
        return errors.New("order ID is required")
    }
    
    if order.Amount <= 0 {
        return errors.New("order amount must be positive")
    }
    
    switch order.Status {
    case "pending":
        return processPendingOrder(order)
    case "confirmed":
        return processConfirmedOrder(order)
    default:
        return fmt.Errorf("unknown order status: %s", order.Status)
    }
}

// Bad: Nested and hard to read
func processOrderBad(order Order) error {
    if order.ID != "" {
        if order.Amount > 0 {
            if order.Status == "pending" {
                return processPendingOrder(order)
            } else {
                if order.Status == "confirmed" {
                    return processConfirmedOrder(order)
                } else {
                    return fmt.Errorf("unknown order status: %s", order.Status)
                }
            }
        } else {
            return errors.New("order amount must be positive")
        }
    } else {
        return errors.New("order ID is required")
    }
}

2. Early Return Pattern

// Good: Early return reduces nesting
func validateUser(user User) error {
    if user.Email == "" {
        return errors.New("email is required")
    }
    
    if !isValidEmail(user.Email) {
        return errors.New("invalid email format")
    }
    
    if user.Age < 13 {
        return errors.New("user must be at least 13 years old")
    }
    
    // Main logic here
    return nil
}

// Bad: Deeply nested conditions
func validateUserBad(user User) error {
    if user.Email != "" {
        if isValidEmail(user.Email) {
            if user.Age >= 13 {
                // Main logic here
                return nil
            } else {
                return errors.New("user must be at least 13 years old")
            }
        } else {
            return errors.New("invalid email format")
        }
    } else {
        return errors.New("email is required")
    }
}

3. Loop Optimization

// Good: Efficient loop with early termination
func findUser(users []User, targetID string) (User, bool) {
    for _, user := range users {
        if user.ID == targetID {
            return user, true
        }
    }
    return User{}, false
}

// Good: Avoid unnecessary work
func processItems(items []Item) {
    if len(items) == 0 {
        return  // Early return for empty slice
    }
    
    for i, item := range items {
        if item.IsDeleted {
            continue  // Skip deleted items
        }
        
        if err := processItem(item); err != nil {
            fmt.Printf("Failed to process item %d: %v\n", i, err)
            continue
        }
    }
}

Selanjutnya kita akan belajar mengenai Functions di Go: Parameter, Return Values, dan Named Returns – Cara kerja fungsi yang berbeda dari bahasa lain.

Seri Belajar Golang – Pemula

  1. Pengenalan Golang: Mengapa Google Menciptakan Bahasa Ini?
  2. Setup Environment dan Hello World di Go
  3. Sintaks Dasar Go: Variables, Constants, dan Data Types
  4. Control Flow di Go: If, Switch, dan Loops
  5. Functions di Go: Parameter, Return Values, dan Named Returns
  6. Arrays, Slices, dan Maps: Struktur Data Penting di Go

Tinggalkan Balasan

Alamat email Anda tidak akan dipublikasikan. Ruas yang wajib ditandai *