【Go语言-Day 7】循环控制全解析:从 for 基础到 for-range 遍历与高级控制

Langchain系列文章目录

01-玩转LangChain:从模型调用到Prompt模板与输出解析的完整指南
02-玩转 LangChain Memory 模块:四种记忆类型详解及应用场景全覆盖
03-全面掌握 LangChain:从核心链条构建到动态任务分配的实战指南
04-玩转 LangChain:从文档加载到高效问答系统构建的全程实战
05-玩转 LangChain:深度评估问答系统的三种高效方法(示例生成、手动评估与LLM辅助评估)
06-从 0 到 1 掌握 LangChain Agents:自定义工具 + LLM 打造智能工作流!
07-【深度解析】从GPT-1到GPT-4:ChatGPT背后的核心原理全揭秘
08-【万字长文】MCP深度解析:打通AI与世界的"USB-C",模型上下文协议原理、实践与未来

Python系列文章目录

PyTorch系列文章目录

机器学习系列文章目录

深度学习系列文章目录

Java系列文章目录

JavaScript系列文章目录

Python系列文章目录

Go语言系列文章目录

01-【Go语言-Day 1】扬帆起航:从零到一,精通 Go 语言环境搭建与首个程序
02-【Go语言-Day 2】代码的基石:深入解析Go变量(var, :=)与常量(const, iota)
03-【Go语言-Day 3】从零掌握 Go 基本数据类型:string, runestrconv 的实战技巧
04-【Go语言-Day 4】掌握标准 I/O:fmt 包 Print, Scan, Printf 核心用法详解
05-【Go语言-Day 5】掌握Go的运算脉络:算术、逻辑到位的全方位指南
06-【Go语言-Day 6】掌控代码流:if-else 条件判断的四种核心用法

07-【Go语言-Day 7】循环控制全解析:从 for 基础到 for-range 遍历与高级控制


文章目录

  • Langchain系列文章目录
  • Python系列文章目录
  • PyTorch系列文章目录
  • 机器学习系列文章目录
  • 深度学习系列文章目录
  • Java系列文章目录
  • JavaScript系列文章目录
  • Python系列文章目录
  • Go语言系列文章目录
  • 前言
  • [一、`for` 循环的基础形态:三段式循环](#一、for 循环的基础形态:三段式循环)
    • [1.1 语法结构与执行流程](#1.1 语法结构与执行流程)
      • [1.1.1 语法定义](#1.1.1 语法定义)
    • [1.2 实例:计算 1 到 100 的和](#1.2 实例:计算 1 到 100 的和)
  • [二、`for` 循环的变种:模拟 `while` 和无限循环](#二、for 循环的变种:模拟 while 和无限循环)
    • [2.1 模拟 `while` 循环](#2.1 模拟 while 循环)
      • [2.1.1 语法结构](#2.1.1 语法结构)
      • [2.1.2 实例:猜数字游戏](#2.1.2 实例:猜数字游戏)
    • [2.2 无限循环 `for {}`](#2.2 无限循环 for {})
      • [2.2.1 语法结构](#2.2.1 语法结构)
      • [2.2.2 应用场景与实例](#2.2.2 应用场景与实例)
  • 三、强大的迭代工具:`for...range`
    • [3.1 `for...range` 语法简介](#3.1 for...range 语法简介)
    • [3.2 遍历不同数据类型](#3.2 遍历不同数据类型)
      • [3.2.1 遍历字符串](#3.2.1 遍历字符串)
      • [3.2.2 遍历数组和切片](#3.2.2 遍历数组和切片)
      • [3.2.3 遍历 `map`](#3.2.3 遍历 map)
      • [3.2.4 遍历通道 (`channel`)](#3.2.4 遍历通道 (channel))
  • 四、循环的流程控制
    • [4.1 `break`:跳出循环](#4.1 break:跳出循环)
        • [(1) 普通 `break`](#(1) 普通 break)
        • [(2) 使用标签 `label` 跳出外层循环](#(2) 使用标签 label 跳出外层循环)
    • [4.2 `continue`:跳过本次迭代](#4.2 continue:跳过本次迭代)
        • [(1) 普通 `continue`](#(1) 普通 continue)
        • [(2) 使用标签 `label` 的 `continue`](#(2) 使用标签 labelcontinue)
    • [4.3 `goto`:谨慎使用的跳转](#4.3 goto:谨慎使用的跳转)
  • 五、总结

前言

大家好,欢迎来到【Go语言从入门到精通】专栏的第 7 篇。在上一篇文章中,我们学习了 Go 语言中的决策者------if-else 条件语句,掌握了如何让程序根据不同条件执行不同路径。今天,我们将深入探讨 Go 语言中一个极其重要且独特的特性:循环结构。

与其他许多编程语言(如 C, Java, Python)不同,Go 语言在设计上追求极致的简洁,因此它只提供了一种循环关键字------for 。然而,千万不要小看这个唯一的循环结构,它的设计非常灵活,足以涵盖其他语言中 forwhiledo-while 等多种循环的功能。

本文将带你全面解锁 for 循环的"百变"用法,从最基础的三段式结构,到模拟 while 循环和无限循环的变体,再到强大的 for...range 迭代利器,最后还会讲解 breakcontinuegoto 这三个循环控制语句。无论你是编程新手还是有一定经验的开发者,相信本文都能让你对 Go 语言的循环有更深刻的理解。

一、for 循环的基础形态:三段式循环

最经典、最常见的 for 循环形式,与 C++ 或 Java 中的 for 循环非常相似,我们称之为"三段式循环"。它将循环的初始化、条件判断和循环后的操作都定义在了一行,结构清晰明了。

1.1 语法结构与执行流程

1.1.1 语法定义

三段式 for 循环由三个部分组成,并用分号 ; 隔开,其基本语法如下:

go 复制代码
for 初始化语句; 条件表达式; 后续语句 {
    // 循环体代码
}
  • 初始化语句 (initialization statement) : 在第一次循环开始前执行,通常用于声明和初始化一个局部循环变量。这个变量的作用域仅限于该 for 循环。
  • 条件表达式 (condition expression) : 在每次循环迭代开始前进行求值。如果表达式的值为 true,则执行循环体;如果为 false,则退出循环。
  • 后续语句 (post-statement): 在每次循环体执行完毕后执行,通常用于更新循环变量(例如,自增或自减)。

这个流程清晰地展示了"初始化 -> 条件判断 -> 执行循环体 -> 执行后续语句 -> 条件判断..."的循环过程。

1.2 实例:计算 1 到 100 的和

这是一个经典的编程入门问题,使用三段式 for 循环解决起来非常方便。

go 复制代码
package main

import "fmt"

func main() {
    sum := 0
    // 使用标准的三段式 for 循环
    // 1. 初始化: i := 1
    // 2. 条件判断: i <= 100
    // 3. 后续语句: i++
    for i := 1; i <= 100; i++ {
        sum += i // 将当前 i 的值累加到 sum
    }
    fmt.Printf("1到100的和是: %d\n", sum) // 输出: 1到100的和是: 5050
}

代码解析

  1. 我们首先初始化一个变量 sum 用于存放累加结果。
  2. for i := 1; i <= 100; i++
    • i := 1:定义并初始化循环变量 i 为 1。
    • i <= 100:只要 i 不超过 100,循环就会继续。
    • i++:每次循环结束后,i 的值加 1。
  3. 循环体 sum += i 将当前的 i 值加到 sum 变量上。
  4. i 增加到 101 时,条件 i <= 100false,循环终止。

二、for 循环的变种:模拟 while 和无限循环

Go 的 for 循环设计非常灵活,通过省略三段式中的某些部分,可以轻松模拟出其他语言中的 while 循环和无限循环。

2.1 模拟 while 循环

在很多语言中,while 循环用于处理那些循环次数不确定,只知道循环终止条件的情况。在 Go 中,我们可以通过省略 for 循环的初始化语句和后续语句来实现同样的效果。

2.1.1 语法结构

go 复制代码
for 条件表达式 {
    // 循环体代码
}

这实际上是三段式循环的一个简化版本,只保留了条件判断部分。

2.1.2 实例:猜数字游戏

让我们看一个简单的例子,程序随机生成一个数字,用户需要不断输入猜测的数字,直到猜对为止。

go 复制代码
package main

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

func main() {
    // 设置随机数种子,否则每次运行结果都一样
    rand.Seed(time.Now().UnixNano())
    secretNumber := rand.Intn(100) + 1 // 生成 1-100 之间的随机数

    var guess int
    fmt.Println("猜一个1到100之间的数字。")

    // 只有条件判断的 for 循环,等价于 while(true)
    // 在这里我们用 for 循环模拟 while 循环,直到猜对为止
    for guess != secretNumber {
        fmt.Print("请输入你猜的数字: ")
        fmt.Scan(&guess) // 从键盘读取输入

        if guess < secretNumber {
            fmt.Println("太小了!")
        } else if guess > secretNumber {
            fmt.Println("太大了!")
        }
    }
    
    fmt.Println("恭喜你,猜对了!")
}

代码解析
for guess != secretNumber 这行代码完美地扮演了 while 循环的角色。只要用户猜测的数字 guess 不等于 secretNumber,循环就会一直持续,不断提示用户输入。

2.2 无限循环 for {}

如果将 for 关键字后的所有部分都省略,就得到了一个无限循环。

2.2.1 语法结构

go 复制代码
for {
    // 永不停止的循环体代码
    // (通常需要一个 break 语句来退出)
}

2.2.2 应用场景与实例

无限循环在需要持续运行的服务中非常常见,例如:

  • Web 服务器监听网络请求。
  • 后台任务持续处理数据。
  • 定时执行某个任务。

通常,无限循环内部会有一个明确的退出条件,通过 break 关键字来实现跳出。

go 复制代码
package main

import (
    "fmt"
    "time"
)

func main() {
    i := 0
    // 无限循环
    for {
        fmt.Println("这是一个无限循环,但我们会在第5次停下来。当前是第", i+1, "次。")
        time.Sleep(500 * time.Millisecond) // 暂停半秒,方便观察

        i++
        if i >= 5 {
            fmt.Println("循环次数达到5次,准备退出。")
            break // 使用 break 关键字跳出无限循环
        }
    }
    fmt.Println("成功退出循环!")
}

代码解析
for {} 创建了一个无限循环。循环体内部通过 if i >= 5 来判断是否满足退出条件,一旦满足,break 语句就会立即终止循环。

三、强大的迭代工具:for...range

for...range 是 Go 语言中一个非常强大且方便的语法结构,专门用于遍历各种数据集合,如字符串、数组、切片、map 和通道(channel)。

3.1 for...range 语法简介

for...range 会从数据集合中依次取出元素,并可以同时返回索引和值。

go 复制代码
for index, value := range collection {
    // 使用 index 和 value
}
  • collection: 要遍历的数据集合。
  • index: 当前元素的索引(对于 map 则是键)。
  • value: 当前元素的值。

如果你不需要索引或值,可以使用匿名变量 _ 来忽略它。

3.2 遍历不同数据类型

3.2.1 遍历字符串

遍历字符串时,for...range 会智能地处理 Unicode 字符,它返回的是每个字符(rune)及其在字符串中的起始字节索引。

go 复制代码
package main

import "fmt"

func main() {
    str := "Go语言-你好"

    // 使用 for...range 遍历字符串
    for index, char := range str {
        // %c 用于打印字符
        fmt.Printf("索引: %d, 字符: %c, Unicode码点: %U\n", index, char, char)
    }
}

输出结果

复制代码
索引: 0, 字符: G, Unicode码点: U+0047
索引: 1, 字符: o, Unicode码点: U+006F
索引: 2, 字符: 语, Unicode码点: U+8BED
索引: 5, 字符: 言, Unicode码点: U+8A00
索引: 8, 字符: -, Unicode码点: U+002D
索引: 9, 字符: 你, Unicode码点: U+4F60
索引: 12, 字符: 好, Unicode码点: U+597D

注意 :中文字符(如"语")占 3 个字节,所以下一个字符"言"的索引从 2 跳到了 5。这正是 for...range 处理多字节字符的优势所在。

3.2.2 遍历数组和切片

遍历数组或切片时,返回的是索引和该索引位置的元素值。

go 复制代码
package main

import "fmt"

func main() {
    nums := []int{10, 20, 30, 40, 50}

    // 完整遍历,获取索引和值
    fmt.Println("--- 获取索引和值 ---")
    for i, v := range nums {
        fmt.Printf("索引: %d, 值: %d\n", i, v)
    }

    // 只关心值,忽略索引
    fmt.Println("\n--- 只获取值 ---")
    for _, v := range nums {
        fmt.Printf("值: %d\n", v)
    }

    // 只关心索引
    fmt.Println("\n--- 只获取索引 ---")
    for i := range nums {
        fmt.Printf("索引: %d\n", i)
    }
}

3.2.3 遍历 map

遍历 map 时,返回的是键和值。需要特别注意的是,map 的遍历顺序是随机的,不保证与插入顺序一致

go 复制代码
package main

import "fmt"

func main() {
    studentScores := map[string]int{
        "Alice": 95,
        "Bob":   88,
        "Cathy": 100,
    }

    for name, score := range studentScores {
        fmt.Printf("学生: %s, 分数: %d\n", name, score)
    }
}

每次运行上述代码,输出的顺序可能会不同。

3.2.4 遍历通道 (channel)

for...range 也可以用于遍历通道(Channel),这在并发编程中非常有用。它会持续从通道中接收数据,直到通道被关闭。我们将在后续的并发编程章节中详细讲解。

四、循环的流程控制

在循环过程中,有时我们需要更精细地控制其流程,例如提前退出或跳过某次迭代。Go 提供了 breakcontinuegoto 来实现这些控制。

4.1 break:跳出循环

break 语句用于立即终止其所在的最内层 forswitchselect 语句的执行。

(1) 普通 break

在单层循环中,break 会直接退出循环。

go 复制代码
package main

import "fmt"

func main() {
    // 寻找 1 到 10 之间第一个能被 3 整除的数
    for i := 1; i <= 10; i++ {
        if i%3 == 0 {
            fmt.Printf("找到了第一个能被3整除的数: %d\n", i)
            break // 找到后立即跳出循环
        }
        fmt.Printf("当前检查的数字是: %d\n", i)
    }
}
(2) 使用标签 label 跳出外层循环

在嵌套循环中,普通的 break 只能跳出内层循环。如果想直接跳出外层循环,需要使用标签 (label)

go 复制代码
package main

import "fmt"

func main() {
MyLoop: // 定义一个标签
    for i := 1; i <= 3; i++ {
        for j := 1; j <= 3; j++ {
            fmt.Printf("i = %d, j = %d\n", i, j)
            if i == 2 && j == 2 {
                fmt.Println("满足条件,使用 break MyLoop 跳出所有循环")
                break MyLoop // 跳出到 MyLoop 标签指定的循环
            }
        }
    }
    fmt.Println("循环结束")
}

输出

复制代码
i = 1, j = 1
i = 1, j = 2
i = 1, j = 3
i = 2, j = 1
i = 2, j = 2
满足条件,使用 break MyLoop 跳出所有循环
循环结束

可以看到,当 i=2, j=2 时,程序直接跳出了最外层的循环。

4.2 continue:跳过本次迭代

continue 语句用于跳过当前循环体的剩余部分,并立即开始下一次迭代。

(1) 普通 continue

continue 在单层循环中会跳过本次迭代。

go 复制代码
package main

import "fmt"

func main() {
    // 打印 1 到 10 之间的所有奇数
    for i := 1; i <= 10; i++ {
        if i%2 == 0 { // 如果是偶数
            continue // 跳过本次循环的剩余部分(即下面的 Printf),直接进入下一次 i++
        }
        fmt.Printf("%d 是奇数\n", i)
    }
}
(2) 使用标签 labelcontinue

break 类似,continue 也可以与标签配合使用,用于控制外层循环的迭代。

4.3 goto:谨慎使用的跳转

goto 语句可以无条件地跳转到当前函数内由标签标识的语句处。

强烈建议:在绝大多数情况下,都应避免使用 goto 因为它会破坏程序的正常执行流程,使代码难以阅读、理解和维护。在现代编程实践中,goto 通常被认为是"代码坏味道"。

不过,为了知识的完整性,我们还是看一个例子,了解其工作方式。

go 复制代码
package main

import "fmt"

func main() {
    fmt.Println("开始执行")
    i := 0

Here: // 定义一个标签
    fmt.Println("当前 i 的值是:", i)
    i++
    if i < 3 {
        goto Here // 无条件跳转到 Here 标签处
    }

    fmt.Println("执行结束")
}

在某些极其复杂的、需要从深度嵌套中一次性跳出的场景,goto 可能是(有争议的)一种选择,但通常使用带标签的 break 是更好、更清晰的方案。

五、总结

在本篇文章中,我们对 Go 语言中唯一的循环结构 for 进行了全面而深入的探讨。现在,让我们来回顾一下核心知识点:

  1. Go 的简洁哲学 :Go 语言仅提供 for 关键字作为循环结构,但其灵活性足以实现传统 forwhile 和无限循环等所有功能。

  2. for 循环的四种形态

    • 三段式 forfor init; condition; post {},用于固定次数的循环,结构清晰。
    • whileforfor condition {},用于循环次数不确定、但有明确终止条件的场景。
    • 无限循环 for {} :用于需要持续运行的服务,通常配合 break 使用。
    • for...range:用于遍历字符串、数组、切片、map 等数据集合,是 Go 中处理迭代的利器,尤其在处理 Unicode 字符串时表现出色。
  3. 循环的流程控制

    • break:用于立即终止循环。配合标签可以跳出嵌套的外层循环。
    • continue:用于跳过当前迭代的剩余部分,直接进入下一次迭代。
    • goto:提供无条件跳转能力,但因其破坏代码结构,应极力避免使用。

掌握 for 循环的各种用法是编写高效、地道 Go 代码的基础。在下一篇文章中,我们将学习 Go 语言中另一个重要的流程控制语句 switch-case,它为我们处理多分支选择提供了比 if-else 更优雅的方案。敬请期待!


相关推荐
GiraKoo6 分钟前
【GiraKoo】C++11的新特性
c++·后端
MO2T11 分钟前
使用 Flask 构建基于 Dify 的企业资金投向与客户分类评估系统
后端·python·语言模型·flask
Naiva14 分钟前
【小技巧】Python + PyCharm 小智AI配置MCP接入点使用说明(内测)( PyInstaller打包成 .exe 可执行文件)
开发语言·python·pycharm
光溯星河18 分钟前
【实践手记】Git重写已提交代码历史信息
后端·github
梦子要转行23 分钟前
matlab/Simulink-全套50个汽车性能建模与仿真源码模型9
开发语言·matlab·汽车
moonless022223 分钟前
🌈Transformer说人话版(二)位置编码 【持续更新ing】
人工智能·llm
小爷毛毛_卓寿杰24 分钟前
基于大模型与知识图谱的对话引导意图澄清系统技术解析
人工智能·llm
聚客AI34 分钟前
解构高效提示工程:分层模型、文本扩展引擎与可视化调试全链路指南
人工智能·llm·掘金·日新计划
PetterHillWater37 分钟前
Trae中实现OOP原则工程重构
后端·aigc
圆滚滚肉肉40 分钟前
后端MVC(控制器与动作方法的关系)
后端·c#·asp.net·mvc