Go 循环语句
for 循环是 Go 语言中极其基础且核心的控制流语句,它能让你重复执行某一段代码。
Go 语言在循环的设计上极其精简:它只有 for 这一种循环结构(没有 while 或 do-while)。但不要被它的单一所欺骗,这个 for 循环极其灵活,能够处理从简单的数字递增到复杂的条件循环等各种场景。
本章将带你深入了解 for 循环的各种形态、功能及其在实际开发中的应用。
1. for 循环的解剖结构
Go 语言标准 for 循环的基础结构包含三个可选的组件,它们之间用分号 ; 隔开:
for 初始化语句; 条件判断; 后置语句 {
// 需要被重复执行的代码
}让我们逐一拆解这三个组件:
- 初始化语句 (Initialization): 这条语句在循环开始之前且仅执行一次。通常用于声明并初始化一个循环计数器变量(例如
i := 0)。 - 条件判断 (Condition): 这是一个布尔表达式,在每一次迭代开始前都会被计算。如果条件为
true,循环继续执行;如果为false,循环立刻终止。 - 后置语句 (Post): 这条语句在每一次迭代结束后立刻执行。它通常用于对循环计数器进行递增或递减操作(例如
i++)。
1.1 基础 for 循环示例
package main
import "fmt"
func main() {
for i := 0; i < 5; i++ { // 初始化: i := 0; 条件: i < 5; 后置: i++
fmt.Println("当前迭代次数:", i)
}
fmt.Println("循环结束")
}在这个例子中:
i := 0将循环计数器i初始化为 0。i < 5是检查条件,只要i小于 5,循环就会一直跑。i++会在每次大括号里的代码执行完毕后,把i的值加 1。- 最终,这段代码会打印从 0 到 4 的迭代信息,然后打印 "循环结束"。
2. 灵活省略结构组件
Go 语言 for 循环的迷人之处在于它的灵活性。你可以省略这三个组件中的任何一个,甚至全部省略。
2.1 省略初始化和后置语句 (类似 while 循环)
如果你把初始化和后置语句都省掉,for 循环就变成了其他语言中的 while 循环:
package main
import "fmt"
func main() {
i := 0
for i < 5 { // 这里只保留了条件判断
fmt.Println("当前迭代次数:", i)
i++ // 在循环体内部手动递增 i
}
fmt.Println("循环结束")
}在这个场景中,初始化 (i := 0) 被移到了循环外部,而后置语句 (i++) 被放到了循环体的内部。它的运行效果和上一个例子完全一样。
2.2 无限循环 (The Infinite Loop)
如果你连条件判断也省略掉,你就创造了一个无限循环(死循环):
package main
import "fmt"
func main() {
for { // 没有初始化、没有条件、没有后置语句
fmt.Println("这行字会永远打印下去(除非你强制中断程序)")
}
}这个循环会无休止地运行下去,除非你在循环内部使用了 break 语句(我们将在下一节讨论),或者强制关闭了程序。无限循环在编写服务器后台监听任务时非常常见。
3. for...range 循环:遍历利器
Go 提供了一种专用于遍历集合(如数组、切片、字符串和 Map)的特殊 for 循环形态:for...range 循环。这是日常开发中使用频率最高的循环方式。
for index, value := range collection {
// 针对集合中的每一个元素,执行这里的代码
}index: 集合中当前元素的索引(位置编号,从 0 开始)。value: 集合中当前元素的值。collection: 你想要遍历的数组、切片、字符串或 Map。
3.1 遍历切片 (Slice)
package main
import "fmt"
func main() {
numbers := []int{10, 20, 30, 40, 50}
for index, value := range numbers {
fmt.Printf("索引: %d, 值: %d\n", index, value)
}
}这个循环会依次提取 numbers 切片中的每一个元素,并打印出它的位置索引和具体数值。
3.2 忽略索引或值
如果你在循环中只关心“值”而不需要用到“索引”,或者反过来,你可以使用空白标识符 _ (下划线) 来忽略它,防止 Go 编译器因为“变量未使用”而报错:
package main
import "fmt"
func main() {
numbers := []int{10, 20, 30, 40, 50}
// 忽略索引 (只要值)
for _, value := range numbers {
fmt.Println("只要值:", value)
}
// 忽略值 (只要索引)
for index := range numbers {
fmt.Println("只要索引:", index)
}
}3.3 遍历字符串 (处理中文与 Unicode)
当使用 for...range 遍历字符串时,Go 会极其智能地按 Rune (Unicode 码点) 而不是纯字节来遍历。这对于包含中文的字符串极其重要!
package main
import "fmt"
func main() {
message := "Hello, 世界" // 包含 ASCII 字符和中文字符
for index, runeValue := range message {
fmt.Printf("字节索引: %d, 字符: %c, Unicode编码: %U\n", index, runeValue, runeValue)
}
}Go 的字符串是 UTF-8 编码的,一个中文字符通常占 3 个字节。for...range 循环能正确识别字符边界,每次迭代返回给你一个完整的字符 (rune),让你安全地处理多语言文本。
4. 实战演练与练习
让我们通过几个实战例子来巩固你的 for 循环技能。
4.1 示例 1:计算数字总和
编写一个程序,使用 for 循环计算切片中所有数字的总和。
package main
import "fmt"
func main() {
numbers := []int{1, 2, 3, 4, 5}
sum := 0
for _, number := range numbers {
sum += number
}
fmt.Println("总和:", sum) // 输出: 15
}4.2 示例 2:找出最大值
编写一个程序,使用 for 循环在切片中找出最大的数字。
package main
import "fmt"
func main() {
numbers := []int{5, 2, 9, 1, 5, 6}
largest := numbers[0] // 假设第一个元素就是最大的
for _, number := range numbers {
if number > largest {
largest = number // 发现更大的,就更新记录
}
}
fmt.Println("最大值:", largest) // 输出: 9
}4.3 挑战练习 1:反转字符串
编写一个函数,利用 for 循环将一个字符串反转。(注意观察这里 for 循环中同时声明和更新两个变量 i,j 的高阶写法)。
package main
import "fmt"
func reverseString(s string) string {
runes := []rune(s) // 将字符串转换为 rune 切片以正确处理字符
// 初始化两个指针:i 从左往右,j 从右往左。当 i < 一半长度时持续循环。
for i, j := 0, len(runes)-1; i < len(runes)/2; i, j = i+1, j-1 {
runes[i], runes[j] = runes[j], runes[i] // 交换首尾对应的字符
}
return string(runes) // 转换回字符串类型
}
func main() {
original := "Go is awesome!"
reversed := reverseString(original)
fmt.Println("原字符串:", original)
fmt.Println("反转后:", reversed)
}4.4 挑战练习 2:统计元音字母数量
编写一个程序,使用 for 循环统计给定字符串中元音字母 (a, e, i, o, u) 的数量。
package main
import (
"fmt"
"strings"
)
func countVowels(s string) int {
vowels := "aeiouAEIOU"
count := 0
for _, char := range s {
// 判断当前字符是否包含在元音字符串中
if strings.ContainsRune(vowels, char) {
count++
}
}
return count
}
func main() {
text := "This is a test string with some vowels."
vowelCount := countVowels(text)
fmt.Println("元音字母的数量:", vowelCount)
}