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
, rune
和 strconv
的实战技巧
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 应用场景与实例)
- [2.1 模拟 `while` 循环](#2.1 模拟
- 三、强大的迭代工具:`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
))
- [3.1 `for...range` 语法简介](#3.1
- 四、循环的流程控制
-
- [4.1 `break`:跳出循环](#4.1
break
:跳出循环) -
-
- [(1) 普通 `break`](#(1) 普通
break
) - [(2) 使用标签 `label` 跳出外层循环](#(2) 使用标签
label
跳出外层循环)
- [(1) 普通 `break`](#(1) 普通
-
- [4.2 `continue`:跳过本次迭代](#4.2
continue
:跳过本次迭代) -
-
- [(1) 普通 `continue`](#(1) 普通
continue
) - [(2) 使用标签 `label` 的 `continue`](#(2) 使用标签
label
的continue
)
- [(1) 普通 `continue`](#(1) 普通
-
- [4.3 `goto`:谨慎使用的跳转](#4.3
goto
:谨慎使用的跳转)
- [4.1 `break`:跳出循环](#4.1
- 五、总结
前言
大家好,欢迎来到【Go语言从入门到精通】专栏的第 7 篇。在上一篇文章中,我们学习了 Go 语言中的决策者------if-else
条件语句,掌握了如何让程序根据不同条件执行不同路径。今天,我们将深入探讨 Go 语言中一个极其重要且独特的特性:循环结构。
与其他许多编程语言(如 C, Java, Python)不同,Go 语言在设计上追求极致的简洁,因此它只提供了一种循环关键字------for
。然而,千万不要小看这个唯一的循环结构,它的设计非常灵活,足以涵盖其他语言中 for
、while
、do-while
等多种循环的功能。
本文将带你全面解锁 for
循环的"百变"用法,从最基础的三段式结构,到模拟 while
循环和无限循环的变体,再到强大的 for...range
迭代利器,最后还会讲解 break
、continue
和 goto
这三个循环控制语句。无论你是编程新手还是有一定经验的开发者,相信本文都能让你对 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
}
代码解析:
- 我们首先初始化一个变量
sum
用于存放累加结果。 for i := 1; i <= 100; i++
:i := 1
:定义并初始化循环变量i
为 1。i <= 100
:只要i
不超过 100,循环就会继续。i++
:每次循环结束后,i
的值加 1。
- 循环体
sum += i
将当前的i
值加到sum
变量上。 - 当
i
增加到 101 时,条件i <= 100
为false
,循环终止。
二、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 提供了 break
、continue
和 goto
来实现这些控制。
4.1 break
:跳出循环
break
语句用于立即终止其所在的最内层 for
、switch
或 select
语句的执行。
(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) 使用标签 label
的 continue
与 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
进行了全面而深入的探讨。现在,让我们来回顾一下核心知识点:
-
Go 的简洁哲学 :Go 语言仅提供
for
关键字作为循环结构,但其灵活性足以实现传统for
、while
和无限循环等所有功能。 -
for
循环的四种形态:- 三段式
for
:for init; condition; post {}
,用于固定次数的循环,结构清晰。 while
式for
:for condition {}
,用于循环次数不确定、但有明确终止条件的场景。- 无限循环
for {}
:用于需要持续运行的服务,通常配合break
使用。 for...range
:用于遍历字符串、数组、切片、map 等数据集合,是 Go 中处理迭代的利器,尤其在处理 Unicode 字符串时表现出色。
- 三段式
-
循环的流程控制:
break
:用于立即终止循环。配合标签可以跳出嵌套的外层循环。continue
:用于跳过当前迭代的剩余部分,直接进入下一次迭代。goto
:提供无条件跳转能力,但因其破坏代码结构,应极力避免使用。
掌握 for
循环的各种用法是编写高效、地道 Go 代码的基础。在下一篇文章中,我们将学习 Go 语言中另一个重要的流程控制语句 switch-case
,它为我们处理多分支选择提供了比 if-else
更优雅的方案。敬请期待!