Go 语言范围(Range)
range 是 Go 语言中用于遍历各种数据结构的关键字。它提供了一种简洁、统一的方式来迭代数组、切片、字符串、映射和通道。
📋 Range 基础语法
基本语法格式
go
// 基本格式
for index, value := range collection {
// 处理 index 和 value
}
// 只需要索引
for index := range collection {
// 只处理 index
}
// 只需要值
for _, value := range collection {
// 只处理 value,使用 _ 忽略索引
}
// 只迭代,不需要索引和值
for range collection {
// 只是迭代,不使用索引和值
}📚 遍历数组和切片
遍历数组
go
package main
import "fmt"
func main() {
// 定义数组
numbers := [5]int{10, 20, 30, 40, 50}
fruits := [3]string{"苹果", "香蕉", "橙子"}
// 完整遍历:索引和值
fmt.Println("=== 遍历数字数组 ===")
for index, value := range numbers {
fmt.Printf("索引: %d, 值: %d\n", index, value)
}
// 只遍历值
fmt.Println("\n=== 只遍历值 ===")
for _, fruit := range fruits {
fmt.Printf("水果: %s\n", fruit)
}
// 计算数组元素总和
sum := 0
for _, num := range numbers {
sum += num
}
fmt.Printf("\n数组总和: %d\n", sum)
// 查找特定元素
target := 30
for i, num := range numbers {
if num == target {
fmt.Printf("找到 %d 在索引 %d\n", target, i)
break
}
}
}遍历切片
go
package main
import "fmt"
func main() {
// 创建切片
slice1 := []int{1, 2, 3, 4, 5}
slice2 := []string{"Go", "Python", "Java"}
// 遍历整数切片
fmt.Println("=== 遍历整数切片 ===")
for i, v := range slice1 {
fmt.Printf("slice1[%d] = %d\n", i, v)
}
// 动态修改切片
fmt.Println("\n=== 修改切片元素 ===")
for i := range slice1 {
slice1[i] = slice1[i] * 2 // 将每个元素乘以2
}
fmt.Printf("修改后的切片: %v\n", slice1)
// 过滤切片元素
var evenNumbers []int
for _, num := range slice1 {
if num%4 == 0 { // 找出能被4整除的数
evenNumbers = append(evenNumbers, num)
}
}
fmt.Printf("能被4整除的数: %v\n", evenNumbers)
}🔤 遍历字符串
字符串遍历
go
package main
import (
"fmt"
"unicode/utf8"
)
func main() {
// 英文字符串
english := "Hello"
fmt.Println("=== 遍历英文字符串 ===")
for i, char := range english {
fmt.Printf("索引: %d, 字符: %c, Unicode: %U\n", i, char, char)
}
// 中文字符串
chinese := "你好世界"
fmt.Println("\n=== 遍历中文字符串 ===")
for i, char := range chinese {
fmt.Printf("字节索引: %d, 字符: %c, Unicode: %U\n", i, char, char)
}
// 字符串统计
text := "Go语言"
fmt.Printf("\n字符串: %s\n", text)
fmt.Printf("字节长度: %d\n", len(text))
fmt.Printf("字符长度: %d\n", utf8.RuneCountInString(text))
// 查找特定字符
target := '语'
for i, char := range text {
if char == target {
fmt.Printf("找到字符 '%c' 在字节位置 %d\n", target, i)
break
}
}
}字符串处理示例
go
package main
import (
"fmt"
"unicode"
)
// 统计字符串中各种字符的数量
func analyzeString(s string) {
var letters, digits, spaces int
for _, char := range s {
switch {
case unicode.IsLetter(char):
letters++
case unicode.IsDigit(char):
digits++
case unicode.IsSpace(char):
spaces++
}
}
fmt.Printf("字符串: \"%s\"\n", s)
fmt.Printf("字母: %d, 数字: %d, 空格: %d\n", letters, digits, spaces)
}
// 反转字符串(支持中文)
func reverseString(s string) string {
runes := []rune(s)
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
runes[i], runes[j] = runes[j], runes[i]
}
return string(runes)
}
func main() {
// 字符串分析
analyzeString("Hello World 123!")
analyzeString("Go语言编程 2023年")
// 字符串反转
fmt.Println("\n=== 字符串反转 ===")
texts := []string{"Hello", "Go语言", "12345"}
for _, text := range texts {
reversed := reverseString(text)
fmt.Printf("原文: %s -> 反转: %s\n", text, reversed)
}
}🗺️ 遍历映射(Map)
基本映射遍历
go
package main
import (
"fmt"
"sort"
)
func main() {
// 创建映射
scores := map[string]int{
"Alice": 95,
"Bob": 87,
"Charlie": 92,
"Diana": 98,
}
// 基本遍历:键和值
fmt.Println("=== 遍历学生成绩 ===")
for name, score := range scores {
fmt.Printf("学生: %s, 成绩: %d\n", name, score)
}
// 计算统计信息
var total, count int
var highest int
var topStudent string
for name, score := range scores {
total += score
count++
if score > highest {
highest = score
topStudent = name
}
}
average := float64(total) / float64(count)
fmt.Printf("\n总分: %d, 平均分: %.2f\n", total, average)
fmt.Printf("最高分: %d (%s)\n", highest, topStudent)
// 按键排序遍历
fmt.Println("\n=== 按姓名排序 ===")
var names []string
for name := range scores {
names = append(names, name)
}
sort.Strings(names)
for _, name := range names {
fmt.Printf("学生: %s, 成绩: %d\n", name, scores[name])
}
}📡 遍历通道(Channel)
通道遍历
go
package main
import (
"fmt"
"time"
)
func main() {
// 创建通道
ch := make(chan int, 5)
// 发送数据到通道
go func() {
for i := 1; i <= 5; i++ {
ch <- i
fmt.Printf("发送: %d\n", i)
time.Sleep(100 * time.Millisecond)
}
close(ch) // 关闭通道
}()
// 使用 range 遍历通道
fmt.Println("=== 遍历通道 ===")
for value := range ch {
fmt.Printf("接收: %d\n", value)
}
fmt.Println("通道已关闭,遍历结束")
}🎯 Range 的陷阱和注意事项
值拷贝陷阱
go
package main
import "fmt"
type Person struct {
Name string
Age int
}
func main() {
people := []Person{
{"Alice", 25},
{"Bob", 30},
}
fmt.Println("=== 值拷贝陷阱 ===")
// 错误的做法:尝试修改拷贝的值
fmt.Println("错误方式(修改拷贝):")
for _, person := range people {
person.Age += 1 // 修改的是拷贝,不是原始数据
}
fmt.Println("原始数据未改变:")
for _, person := range people {
fmt.Printf("%s, 年龄: %d\n", person.Name, person.Age)
}
// 正确的做法:使用索引修改
fmt.Println("\n正确方式(使用索引):")
for i := range people {
people[i].Age += 1
}
fmt.Println("原始数据已改变:")
for _, person := range people {
fmt.Printf("%s, 年龄: %d\n", person.Name, person.Age)
}
}闭包陷阱
go
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println("=== 闭包陷阱 ===")
// 错误的做法:闭包捕获循环变量
var funcs []func()
for _, name := range []string{"Alice", "Bob", "Charlie"} {
funcs = append(funcs, func() {
fmt.Printf("错误方式: %s\n", name) // 所有闭包都会打印最后一个值
})
}
fmt.Println("错误的闭包结果:")
for _, f := range funcs {
f()
}
// 正确的做法:传递参数
funcs = nil
for _, name := range []string{"Alice", "Bob", "Charlie"} {
funcs = append(funcs, func(n string) func() {
return func() {
fmt.Printf("正确方式: %s\n", n)
}
}(name))
}
fmt.Println("\n正确的闭包结果:")
for _, f := range funcs {
f()
}
// Goroutine 闭包陷阱
fmt.Println("\nGoroutine 中的应用:")
// 正确方式:传递参数给 goroutine
for _, name := range []string{"Alice", "Bob", "Charlie"} {
go func(n string) {
time.Sleep(10 * time.Millisecond)
fmt.Printf("Goroutine: %s\n", n)
}(name)
}
time.Sleep(50 * time.Millisecond)
}🛠️ Range 实际应用示例
数据统计分析
go
package main
import "fmt"
// 学生成绩统计
func analyzeScores(scores map[string]int) {
if len(scores) == 0 {
fmt.Println("没有成绩数据")
return
}
var total, count int
var max, min int
var maxStudent, minStudent string
// 初始化最大最小值
first := true
for name, score := range scores {
if first {
max, min = score, score
maxStudent, minStudent = name, name
first = false
}
total += score
count++
if score > max {
max = score
maxStudent = name
}
if score < min {
min = score
minStudent = name
}
}
average := float64(total) / float64(count)
fmt.Println("=== 成绩统计 ===")
fmt.Printf("学生人数: %d\n", count)
fmt.Printf("平均分: %.2f\n", average)
fmt.Printf("最高分: %d (%s)\n", max, maxStudent)
fmt.Printf("最低分: %d (%s)\n", min, minStudent)
// 分级统计
excellent, good, pass, fail := 0, 0, 0, 0
for _, score := range scores {
switch {
case score >= 90:
excellent++
case score >= 80:
good++
case score >= 60:
pass++
default:
fail++
}
}
fmt.Printf("优秀(90+): %d人\n", excellent)
fmt.Printf("良好(80-89): %d人\n", good)
fmt.Printf("及格(60-79): %d人\n", pass)
fmt.Printf("不及格(<60): %d人\n", fail)
}
// 单词频率统计
func wordFrequency(text string) map[rune]int {
frequency := make(map[rune]int)
for _, char := range text {
if char != ' ' { // 忽略空格
frequency[char]++
}
}
return frequency
}
func main() {
// 成绩统计示例
scores := map[string]int{
"张三": 95,
"李四": 87,
"王五": 92,
"赵六": 78,
"钱七": 96,
}
analyzeScores(scores)
// 字符频率统计
fmt.Println("\n=== 字符频率统计 ===")
text := "Hello World Go语言"
freq := wordFrequency(text)
fmt.Printf("文本: %s\n", text)
fmt.Println("字符频率:")
for char, count := range freq {
fmt.Printf(" '%c': %d次\n", char, count)
}
}🎓 小结
本章我们全面学习了 Go 语言的 range 关键字:
- ✅ 基础语法:for-range 的各种使用形式
- ✅ 数据遍历:数组、切片、字符串、映射、通道
- ✅ 字符串处理:Unicode 字符遍历和处理
- ✅ 常见陷阱:值拷贝和闭包陷阱
- ✅ 实际应用:数据统计分析案例
range 是 Go 语言中非常重要和常用的特性,掌握其正确使用方法对于编写高效的 Go 代码至关重要。
接下来,我们将学习 Go 语言递归函数,探索递归编程的强大功能。
Range 使用建议
- 注意值拷贝问题,需要修改原数据时使用索引
- 避免在闭包中直接引用循环变量
- 遍历字符串时注意 Unicode 字符的字节索引
- 合理使用 range 提高代码可读性