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:跳过当前迭代,支持标签
实际应用 #
- 数据处理:统计、分析、转换
- 算法实现:排序、查找、数学计算
- 文件处理:解析、分析、转换
性能优化 #
- 避免重复计算
- 合理选择循环方式
- 预先计算循环条件
掌握循环语句的使用是编程的重要技能,为后续学习函数和复杂算法奠定了基础。
练习题:
- 编写程序计算并输出九九乘法表
- 实现一个简单的猜数字游戏,包含提示功能
- 编写程序统计一段文本中各个字符的出现频率
- 实现冒泡排序和快速排序算法
- 编写一个简单的日志分析器,统计不同级别日志的数量