1.3.2 循环语句

1.3.2 循环语句 #

循环语句是程序中重复执行代码块的重要控制结构。Go 语言只提供了一种循环语句 for,但它的语法灵活多样,可以实现各种循环需求。本节将详细介绍 Go 语言中循环语句的各种用法。

for 循环的基本形式 #

1. 传统的三段式 for 循环 #

这是最常见的循环形式,包含初始化、条件判断和后置语句:

package main

import "fmt"

func main() {
    // 基本的 for 循环
    fmt.Println("基本 for 循环:")
    for i := 0; i < 5; i++ {
        fmt.Printf("第 %d 次循环\n", i+1)
    }

    // 计算 1 到 100 的和
    sum := 0
    for i := 1; i <= 100; i++ {
        sum += i
    }
    fmt.Printf("1 到 100 的和: %d\n", sum)

    // 倒序循环
    fmt.Println("倒序循环:")
    for i := 10; i > 0; i-- {
        fmt.Printf("%d ", i)
    }
    fmt.Println("发射!")

    // 步长不为 1 的循环
    fmt.Println("偶数输出:")
    for i := 0; i <= 20; i += 2 {
        fmt.Printf("%d ", i)
    }
    fmt.Println()
}

2. 条件循环(类似 while) #

Go 语言没有 while 关键字,但可以用 for 实现相同功能:

package main

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

func main() {
    // 类似 while 循环
    fmt.Println("猜数字游戏:")
    rand.Seed(time.Now().UnixNano())
    target := rand.Intn(100) + 1
    guess := 0
    attempts := 0

    for guess != target {
        attempts++
        guess = rand.Intn(100) + 1
        fmt.Printf("第 %d 次尝试: %d", attempts, guess)

        if guess < target {
            fmt.Println(" (太小了)")
        } else if guess > target {
            fmt.Println(" (太大了)")
        } else {
            fmt.Println(" (猜对了!)")
        }
    }

    fmt.Printf("总共尝试了 %d 次\n", attempts)

    // 读取用户输入直到满足条件
    fmt.Println("\n输入验证示例:")
    var password string
    for {
        fmt.Print("请输入密码 (至少6位): ")
        fmt.Scanln(&password)

        if len(password) >= 6 {
            fmt.Println("密码设置成功!")
            break
        }
        fmt.Println("密码太短,请重新输入")
    }
}

3. 无限循环 #

package main

import (
    "fmt"
    "time"
)

func main() {
    // 无限循环示例
    fmt.Println("服务器启动...")

    counter := 0
    for {
        counter++
        fmt.Printf("处理请求 #%d\n", counter)

        // 模拟处理时间
        time.Sleep(1 * time.Second)

        // 设置退出条件
        if counter >= 5 {
            fmt.Println("服务器关闭")
            break
        }
    }

    // 另一个无限循环示例:简单的菜单系统
    fmt.Println("\n简单菜单系统:")
    for {
        fmt.Println("\n请选择操作:")
        fmt.Println("1. 查看信息")
        fmt.Println("2. 修改设置")
        fmt.Println("3. 退出程序")
        fmt.Print("请输入选择 (1-3): ")

        var choice int
        fmt.Scanln(&choice)

        switch choice {
        case 1:
            fmt.Println("显示系统信息...")
        case 2:
            fmt.Println("修改系统设置...")
        case 3:
            fmt.Println("程序退出")
            return
        default:
            fmt.Println("无效选择,请重新输入")
        }
    }
}

range 循环 #

1. 遍历数组和切片 #

range 关键字用于遍历各种数据结构:

package main

import "fmt"

func main() {
    // 遍历数组
    numbers := [5]int{10, 20, 30, 40, 50}

    fmt.Println("遍历数组 (索引和值):")
    for index, value := range numbers {
        fmt.Printf("索引 %d: 值 %d\n", index, value)
    }

    // 只要值,不要索引
    fmt.Println("\n只遍历值:")
    for _, value := range numbers {
        fmt.Printf("%d ", value)
    }
    fmt.Println()

    // 只要索引,不要值
    fmt.Println("只遍历索引:")
    for index := range numbers {
        fmt.Printf("索引 %d ", index)
    }
    fmt.Println()

    // 遍历切片
    fruits := []string{"苹果", "香蕉", "橙子", "葡萄"}

    fmt.Println("\n遍历切片:")
    for i, fruit := range fruits {
        fmt.Printf("%d. %s\n", i+1, fruit)
    }

    // 计算切片元素总和
    scores := []int{85, 92, 78, 96, 88}
    total := 0

    for _, score := range scores {
        total += score
    }

    average := float64(total) / float64(len(scores))
    fmt.Printf("平均分: %.2f\n", average)
}

2. 遍历字符串 #

package main

import (
    "fmt"
    "unicode"
)

func main() {
    text := "Hello, 世界!"

    // 按字节遍历
    fmt.Println("按字节遍历:")
    for i := 0; i < len(text); i++ {
        fmt.Printf("字节 %d: %c (0x%02X)\n", i, text[i], text[i])
    }

    // 按字符遍历 (推荐)
    fmt.Println("\n按字符遍历:")
    for index, char := range text {
        fmt.Printf("位置 %d: %c (Unicode: U+%04X)\n", index, char, char)
    }

    // 字符统计示例
    fmt.Println("\n字符统计:")
    letterCount := 0
    digitCount := 0
    spaceCount := 0
    otherCount := 0

    for _, char := range text {
        switch {
        case unicode.IsLetter(char):
            letterCount++
        case unicode.IsDigit(char):
            digitCount++
        case unicode.IsSpace(char):
            spaceCount++
        default:
            otherCount++
        }
    }

    fmt.Printf("字母: %d, 数字: %d, 空格: %d, 其他: %d\n",
               letterCount, digitCount, spaceCount, otherCount)
}

3. 遍历 Map #

package main

import (
    "fmt"
    "sort"
)

func main() {
    // 创建并初始化 map
    studentGrades := map[string]int{
        "张三": 85,
        "李四": 92,
        "王五": 78,
        "赵六": 96,
        "钱七": 88,
    }

    // 遍历 map
    fmt.Println("学生成绩:")
    for name, grade := range studentGrades {
        fmt.Printf("%s: %d分\n", name, grade)
    }

    // 只遍历键
    fmt.Println("\n学生名单:")
    for name := range studentGrades {
        fmt.Printf("- %s\n", name)
    }

    // 只遍历值
    fmt.Println("\n成绩列表:")
    for _, grade := range studentGrades {
        fmt.Printf("%d ", grade)
    }
    fmt.Println()

    // 按键排序遍历
    fmt.Println("\n按姓名排序:")
    var names []string
    for name := range studentGrades {
        names = append(names, name)
    }
    sort.Strings(names)

    for _, name := range names {
        fmt.Printf("%s: %d分\n", name, studentGrades[name])
    }

    // 统计成绩分布
    gradeRanges := map[string]int{
        "优秀(90-100)": 0,
        "良好(80-89)":  0,
        "中等(70-79)":  0,
        "及格(60-69)":  0,
        "不及格(0-59)": 0,
    }

    for _, grade := range studentGrades {
        switch {
        case grade >= 90:
            gradeRanges["优秀(90-100)"]++
        case grade >= 80:
            gradeRanges["良好(80-89)"]++
        case grade >= 70:
            gradeRanges["中等(70-79)"]++
        case grade >= 60:
            gradeRanges["及格(60-69)"]++
        default:
            gradeRanges["不及格(0-59)"]++
        }
    }

    fmt.Println("\n成绩分布:")
    for range_name, count := range gradeRanges {
        if count > 0 {
            fmt.Printf("%s: %d人\n", range_name, count)
        }
    }
}

循环控制语句 #

1. break 语句 #

break 用于跳出循环:

package main

import "fmt"

func main() {
    // 在 for 循环中使用 break
    fmt.Println("查找第一个偶数:")
    numbers := []int{1, 3, 5, 8, 9, 12, 15}

    for i, num := range numbers {
        if num%2 == 0 {
            fmt.Printf("找到第一个偶数: %d (位置: %d)\n", num, i)
            break
        }
    }

    // 嵌套循环中的 break
    fmt.Println("\n九九乘法表 (只显示前5行):")
    for i := 1; i <= 9; i++ {
        if i > 5 {
            break
        }
        for j := 1; j <= i; j++ {
            fmt.Printf("%d×%d=%d ", j, i, i*j)
        }
        fmt.Println()
    }

    // 带标签的 break
    fmt.Println("\n带标签的 break 示例:")
outer:
    for i := 1; i <= 3; i++ {
        for j := 1; j <= 3; j++ {
            if i*j > 4 {
                fmt.Printf("当 i=%d, j=%d 时跳出外层循环\n", i, j)
                break outer
            }
            fmt.Printf("i=%d, j=%d, i*j=%d\n", i, j, i*j)
        }
    }
}

2. continue 语句 #

continue 用于跳过当前循环迭代:

package main

import "fmt"

func main() {
    // 跳过偶数,只打印奇数
    fmt.Println("1到20的奇数:")
    for i := 1; i <= 20; i++ {
        if i%2 == 0 {
            continue
        }
        fmt.Printf("%d ", i)
    }
    fmt.Println()

    // 处理数据时跳过无效值
    fmt.Println("\n处理学生成绩 (跳过无效成绩):")
    grades := []int{85, -1, 92, 0, 78, 105, 88, -5}

    validGrades := []int{}
    for _, grade := range grades {
        if grade < 0 || grade > 100 {
            fmt.Printf("跳过无效成绩: %d\n", grade)
            continue
        }
        validGrades = append(validGrades, grade)
    }

    fmt.Printf("有效成绩: %v\n", validGrades)

    // 带标签的 continue
    fmt.Println("\n带标签的 continue 示例:")
outer:
    for i := 1; i <= 3; i++ {
        fmt.Printf("外层循环 i=%d:\n", i)
        for j := 1; j <= 5; j++ {
            if j%2 == 0 {
                continue outer
            }
            fmt.Printf("  内层循环 j=%d\n", j)
        }
    }
}

循环的实际应用 #

1. 数据处理和统计 #

package main

import (
    "fmt"
    "math"
)

// 学生信息结构
type Student struct {
    Name   string
    Scores []int
}

func main() {
    students := []Student{
        {"张三", []int{85, 92, 78, 88, 95}},
        {"李四", []int{76, 88, 92, 85, 90}},
        {"王五", []int{95, 87, 91, 89, 93}},
        {"赵六", []int{82, 79, 85, 88, 86}},
    }

    fmt.Println("学生成绩统计:")
    fmt.Println("=" * 50)

    for _, student := range students {
        // 计算总分和平均分
        total := 0
        for _, score := range student.Scores {
            total += score
        }
        average := float64(total) / float64(len(student.Scores))

        // 找出最高分和最低分
        maxScore := student.Scores[0]
        minScore := student.Scores[0]
        for _, score := range student.Scores {
            if score > maxScore {
                maxScore = score
            }
            if score < minScore {
                minScore = score
            }
        }

        // 计算标准差
        var variance float64
        for _, score := range student.Scores {
            variance += math.Pow(float64(score)-average, 2)
        }
        variance /= float64(len(student.Scores))
        stdDev := math.Sqrt(variance)

        fmt.Printf("学生: %s\n", student.Name)
        fmt.Printf("  成绩: %v\n", student.Scores)
        fmt.Printf("  总分: %d\n", total)
        fmt.Printf("  平均分: %.2f\n", average)
        fmt.Printf("  最高分: %d\n", maxScore)
        fmt.Printf("  最低分: %d\n", minScore)
        fmt.Printf("  标准差: %.2f\n", stdDev)
        fmt.Println()
    }
}

2. 算法实现 #

package main

import "fmt"

// 冒泡排序
func bubbleSort(arr []int) {
    n := len(arr)
    for i := 0; i < n-1; i++ {
        swapped := false
        for j := 0; j < n-i-1; j++ {
            if arr[j] > arr[j+1] {
                arr[j], arr[j+1] = arr[j+1], arr[j]
                swapped = true
            }
        }
        // 如果没有交换,说明已经排序完成
        if !swapped {
            break
        }
    }
}

// 选择排序
func selectionSort(arr []int) {
    n := len(arr)
    for i := 0; i < n-1; i++ {
        minIdx := i
        for j := i + 1; j < n; j++ {
            if arr[j] < arr[minIdx] {
                minIdx = j
            }
        }
        arr[i], arr[minIdx] = arr[minIdx], arr[i]
    }
}

// 二分查找
func binarySearch(arr []int, target int) int {
    left, right := 0, len(arr)-1

    for left <= right {
        mid := left + (right-left)/2

        if arr[mid] == target {
            return mid
        } else if arr[mid] < target {
            left = mid + 1
        } else {
            right = mid - 1
        }
    }

    return -1
}

// 计算斐波那契数列
func fibonacci(n int) []int {
    if n <= 0 {
        return []int{}
    }
    if n == 1 {
        return []int{0}
    }

    fib := make([]int, n)
    fib[0], fib[1] = 0, 1

    for i := 2; i < n; i++ {
        fib[i] = fib[i-1] + fib[i-2]
    }

    return fib
}

// 判断是否为质数
func isPrime(n int) bool {
    if n < 2 {
        return false
    }

    for i := 2; i*i <= n; i++ {
        if n%i == 0 {
            return false
        }
    }

    return true
}

// 生成质数列表
func generatePrimes(limit int) []int {
    var primes []int

    for i := 2; i <= limit; i++ {
        if isPrime(i) {
            primes = append(primes, i)
        }
    }

    return primes
}

func main() {
    // 测试排序算法
    fmt.Println("排序算法测试:")

    arr1 := []int{64, 34, 25, 12, 22, 11, 90}
    fmt.Printf("原数组: %v\n", arr1)

    bubbleSort(arr1)
    fmt.Printf("冒泡排序后: %v\n", arr1)

    arr2 := []int{64, 34, 25, 12, 22, 11, 90}
    selectionSort(arr2)
    fmt.Printf("选择排序后: %v\n", arr2)

    // 测试二分查找
    fmt.Println("\n二分查找测试:")
    sortedArr := []int{1, 3, 5, 7, 9, 11, 13, 15, 17, 19}
    target := 7
    index := binarySearch(sortedArr, target)

    if index != -1 {
        fmt.Printf("在数组 %v 中找到 %d,位置: %d\n", sortedArr, target, index)
    } else {
        fmt.Printf("在数组 %v 中未找到 %d\n", sortedArr, target)
    }

    // 测试斐波那契数列
    fmt.Println("\n斐波那契数列:")
    fibNumbers := fibonacci(10)
    fmt.Printf("前10个斐波那契数: %v\n", fibNumbers)

    // 测试质数生成
    fmt.Println("\n质数生成:")
    primes := generatePrimes(50)
    fmt.Printf("50以内的质数: %v\n", primes)
}

3. 文件处理和数据解析 #

package main

import (
    "fmt"
    "strings"
)

// 模拟 CSV 数据处理
func processCSVData(csvData string) {
    lines := strings.Split(csvData, "\n")

    if len(lines) == 0 {
        fmt.Println("没有数据")
        return
    }

    // 处理表头
    headers := strings.Split(lines[0], ",")
    fmt.Printf("表头: %v\n", headers)

    // 处理数据行
    fmt.Println("数据:")
    for i := 1; i < len(lines); i++ {
        if strings.TrimSpace(lines[i]) == "" {
            continue
        }

        fields := strings.Split(lines[i], ",")
        if len(fields) != len(headers) {
            fmt.Printf("第 %d 行数据格式错误: %s\n", i+1, lines[i])
            continue
        }

        fmt.Printf("第 %d 行: ", i)
        for j, field := range fields {
            fmt.Printf("%s=%s ", headers[j], strings.TrimSpace(field))
        }
        fmt.Println()
    }
}

// 日志分析示例
func analyzeLogData(logData []string) {
    errorCount := 0
    warningCount := 0
    infoCount := 0

    errorMessages := make(map[string]int)

    fmt.Println("日志分析:")

    for lineNum, line := range logData {
        line = strings.TrimSpace(line)
        if line == "" {
            continue
        }

        switch {
        case strings.Contains(line, "ERROR"):
            errorCount++
            // 提取错误信息
            parts := strings.Split(line, "ERROR:")
            if len(parts) > 1 {
                errorMsg := strings.TrimSpace(parts[1])
                errorMessages[errorMsg]++
            }

        case strings.Contains(line, "WARNING"):
            warningCount++

        case strings.Contains(line, "INFO"):
            infoCount++
        }

        // 显示处理进度
        if (lineNum+1)%100 == 0 {
            fmt.Printf("已处理 %d 行日志\n", lineNum+1)
        }
    }

    fmt.Printf("\n日志统计:\n")
    fmt.Printf("总行数: %d\n", len(logData))
    fmt.Printf("错误: %d\n", errorCount)
    fmt.Printf("警告: %d\n", warningCount)
    fmt.Printf("信息: %d\n", infoCount)

    if len(errorMessages) > 0 {
        fmt.Println("\n错误信息统计:")
        for msg, count := range errorMessages {
            fmt.Printf("  %s: %d次\n", msg, count)
        }
    }
}

func main() {
    // CSV 数据处理示例
    csvData := `姓名,年龄,城市,职业
张三,25,北京,工程师
李四,30,上海,设计师
王五,28,广州,产品经理
赵六,35,深圳,项目经理`

    fmt.Println("CSV 数据处理:")
    processCSVData(csvData)

    // 日志分析示例
    fmt.Println("\n" + strings.Repeat("=", 50))

    logData := []string{
        "2024-01-01 10:00:01 INFO: 系统启动",
        "2024-01-01 10:00:02 INFO: 加载配置文件",
        "2024-01-01 10:00:03 WARNING: 配置文件中缺少某些参数",
        "2024-01-01 10:00:04 INFO: 连接数据库",
        "2024-01-01 10:00:05 ERROR: 数据库连接失败",
        "2024-01-01 10:00:06 INFO: 重试连接数据库",
        "2024-01-01 10:00:07 INFO: 数据库连接成功",
        "2024-01-01 10:00:08 ERROR: 用户认证失败",
        "2024-01-01 10:00:09 WARNING: 磁盘空间不足",
        "2024-01-01 10:00:10 ERROR: 数据库连接失败",
    }

    analyzeLogData(logData)
}

循环性能优化 #

1. 避免在循环中进行重复计算 #

package main

import (
    "fmt"
    "time"
)

func main() {
    data := make([]int, 1000000)
    for i := range data {
        data[i] = i
    }

    // 不好的做法:在循环中重复计算
    start := time.Now()
    sum1 := 0
    for i := 0; i < len(data); i++ {
        if i < len(data)/2 { // 每次都计算 len(data)/2
            sum1 += data[i]
        }
    }
    duration1 := time.Since(start)

    // 好的做法:预先计算
    start = time.Now()
    sum2 := 0
    halfLen := len(data) / 2 // 预先计算
    for i := 0; i < halfLen; i++ {
        sum2 += data[i]
    }
    duration2 := time.Since(start)

    fmt.Printf("重复计算方式: %v, 结果: %d\n", duration1, sum1)
    fmt.Printf("预先计算方式: %v, 结果: %d\n", duration2, sum2)
    fmt.Printf("性能提升: %.2fx\n", float64(duration1)/float64(duration2))
}

2. 合理使用 range 和索引循环 #

package main

import (
    "fmt"
    "time"
)

func main() {
    // 创建大数组
    size := 1000000
    data := make([]int, size)
    for i := range data {
        data[i] = i
    }

    // 使用 range 遍历(推荐用于需要值的情况)
    start := time.Now()
    sum1 := 0
    for _, value := range data {
        sum1 += value
    }
    duration1 := time.Since(start)

    // 使用索引遍历(推荐用于需要索引的情况)
    start = time.Now()
    sum2 := 0
    for i := 0; i < len(data); i++ {
        sum2 += data[i]
    }
    duration2 := time.Since(start)

    fmt.Printf("range 遍历: %v, 结果: %d\n", duration1, sum1)
    fmt.Printf("索引遍历: %v, 结果: %d\n", duration2, sum2)
}

小结 #

本节详细介绍了 Go 语言的循环语句,主要内容包括:

for 循环的形式 #

  • 三段式循环:初始化、条件、后置语句
  • 条件循环:类似其他语言的 while 循环
  • 无限循环:用于持续运行的程序

range 循环 #

  • 遍历数组/切片:获取索引和值
  • 遍历字符串:按字符遍历,处理 Unicode
  • 遍历 Map:获取键值对

循环控制 #

  • break:跳出循环,支持标签
  • continue:跳过当前迭代,支持标签

实际应用 #

  • 数据处理:统计、分析、转换
  • 算法实现:排序、查找、数学计算
  • 文件处理:解析、分析、转换

性能优化 #

  • 避免重复计算
  • 合理选择循环方式
  • 预先计算循环条件

掌握循环语句的使用是编程的重要技能,为后续学习函数和复杂算法奠定了基础。


练习题:

  1. 编写程序计算并输出九九乘法表
  2. 实现一个简单的猜数字游戏,包含提示功能
  3. 编写程序统计一段文本中各个字符的出现频率
  4. 实现冒泡排序和快速排序算法
  5. 编写一个简单的日志分析器,统计不同级别日志的数量