您的当前位置:首页正文

Go 控制结构

2024-11-24 来源:个人技术集锦

Go 控制结构

上一篇:
下一篇:



前言

Go 的控制结构与C 的控制结构相关,但在一些重要方面有所不同

  • Go 没有 dowhile 循环,只有稍微泛化的 for 循环;切换更加灵活
  • ifswitch 接受一个可选的初始化语句,类似于for
  • breakcontinue 语句接受一个可选的标签,用于标识 break 跳出位置和 continue 转到继续执行的位置
  • 新的控制结构包括 switch obj.(type) 和多路通信复用器 select。这两个控制结构分别在 [[200-编程语言/Go/基础/反射|反射]] 中介绍 switch obj.(type) 和 [[Channels]] 中介绍

它们的语法也略有不同;没有括号,并且 主体必须总是用大括号分隔

一 条件语句

在 Go 中,有如下条件控制语句

  • if 语句:如果一个条件为true,则执行一些代码
  • if-else 语句:如果条件为 true,执行一些代码,如果条件为 false ,执行另一段代码
  • if-else if-else 语句:为两个以上的条件执行不同的代码
  • switch....case 语句:选择要执行的众多代码块中的一个

1.1 if 语句

if 语句仅在指定的条件求值为 true 时执行代码块

if condition { 
    // condition 为 true 时执行的代码
}

如果xtrue,下面的例子将输出 "Japan":

package main

import "fmt"

func main() {
	var s = "Japan"
	x := true
	if x {
		fmt.Println(s)
	}
}

1.2 if-else 语句

if....else 语句允许您在指定条件为 true 时执行一个代码块,在指定条件为 false 时执行另一个代码块

if condition { 
    // condition 为 true 时执行的代码
} else {
    // condition 为 false 时执行的代码
}

[!ERROR] 注意
} 后面放 else { 也是强制性的,如果不这样做,也将抛出 syntax 错误

如果 x100,下面的示例将输出 "Japan",否则输出 Canada

package main
 
import "fmt"
 
func main() {
	x := 100
	if x == 100 {
		fmt.Println("Japan")
	} else {
		fmt.Println("Canada")
	}
}

1.3 if-else if-else 语句

if...else if...else 语句是多个 if...else 语句的嵌套

if  condition-1 { 
    // 如果condition-1 为 true,将执行的代码
} else if condition-2 {
    // 如果condition-2 为 true,将执行的代码
} else {
    // 如果condition-1 和condition-2 均为 false,将执行的代码
}

如果 x100,下面的示例将输出 "Japan"。如果 x50,将输出 Germany 其他情况将输出 Canada

package main
 
import (
	"fmt"
)
 
func main() {
	x := 100
 
	if x == 50 {
		fmt.Println("Germany")
	} else if x == 100 {
		fmt.Println("Japan")
	} else {
		fmt.Println("Canada")
	}
}

1.4 if 语句初始化

if 语句支持复合语法,在这种语法中,被测试的表达式前面有一个初始化语句

if  var declaration; condition { 
    // condition 为 true 时执行的代码
}

如果 x100,下面的例子将输出"Germany":

package main
 
import (
	"fmt"
)
 
func main() {
	if x := 100; x == 100 {
		fmt.Println("Germany")
	}
}
  • 这个 x 只能在 if 语句组中使用,离开 if 语句组,就无法在使用了

1.5 switch 语句

switch 语句用于 从多个代码块中选择一个执行。它的完整语法如下

switch initializer; expr {
	case value1:
		<statement-1>
		<....>
		<statement-n>
	case value2:
		<statement-1>
		<....>
		<statement-n>
	....
	default:
		<statement-1>
		<....>
		<statement-n>
}
  • initializer 是一个简单的初始化语句,可以省略
  • expr 是一个表达式,value 的值的类型必须与 expr 表达式的类型相同

[!ERROR] 错误

所有的 case 值都应该与 switch 后的表达式的值的类型相同,否则将抛出错误

  • 不像 C 那样要求 expr 必须是整数

考虑以下示例,它在特定日期显示不同的消息

package main  
  
import (  
    "fmt"  
    "time")  
  
func main() {  
    today := time.Now()  
  
    switch /*初始化语句;*/ today.Day() {  
    case 5:
       fmt.Println("Today is 5th. Clean your house.")  
    case 10:  
       fmt.Println("Today is 10th. Buy some wine.")  
    case 15:  
       fmt.Println("Today is 15th. Visit a doctor.")  
    case 25:  
       fmt.Println("Today is 25th. Buy some food.")  
    case 31:  
       fmt.Println("Party tonight.")  
    default:  
       fmt.Println("No information available for that day.")  
    }  
}
  • 如果找不到匹配的 case 项,则会执行 default 语句

1.6 多 cases switch

带有多 case 行语句的 switch 用于为许多类似情况选择共同的代码块

package main

import (
	"fmt"
	"time"
)

func main() {
	today := time.Now()
	var t int = today.Day()

	switch /*初始化语句;*/ t {
	case 5, 10, 15:
		fmt.Println("Clean your house.")
	case 25, 26, 27:
		fmt.Println("Buy some food.")
	case 31:
		fmt.Println("Party tonight.")
	default:
		fmt.Println("No information available for that day.")
	}
}

1.7 fallthrough caseswitch

fallthrough 关键字用于 强制控制通过连续的 case

package main

import (
	"fmt"
	"time"
)

func main() {
	today := time.Now()

	switch /*初始化语句;*/ today.Day() {
	case 5:
		fmt.Println("Clean your house.")
		fallthrough // 强制控制流转到下一个 case 语句组
	case 10:
		fmt.Println("Buy some wine.")
		fallthrough //强制控制流转到下一个 case 语句组
	case 15:
		fmt.Println("Visit a doctor.")
		fallthrough // 强制控制流转到下一个 case 语句组
	case 25:
		fmt.Println("Buy some food.")
		fallthrough // 强制控制流转到下一个 case 语句组
	case 31:
		fmt.Println("Party tonight.")
	default:
		fmt.Println("No information available for that day.")
	}
}

1.8 条件 caseswith

case 语句也可以与比较运算符和逻辑运算符一起使用

package main

import (
	"fmt"
	"time"
)

func main() {
	today := time.Now()

	switch /*初始化语句;*/ { // 此处没有表达式
	case today.Day() < 5: // case 一个条件
		fmt.Println("Clean your house.")
	case today.Day() <= 10: // case 一个条件
		fmt.Println("Buy some wine.")
	case today.Day() > 15: // case 一个条件
		fmt.Println("Visit a doctor.")
	case today.Day() == 25: // case 一个条件
		fmt.Println("Buy some food.")
	default:
		fmt.Println("No information available for that day.")
	}
}

1.9 switch 初始化

switch 关键字后面可以紧接一个简单的初始化语句,其中可以声明和初始化 switch 代码块中的局部变量

package main

import (
	"fmt"
	"time"
)

func main() {
	switch today := time.Now(); /*expr*/{
	case today.Day() < 5:
		fmt.Println("Clean your house.")
	case today.Day() <= 10:
		fmt.Println("Buy some wine.")
	case today.Day() > 15:
		fmt.Println("Visit a doctor.")
	case today.Day() == 25:
		fmt.Println("Buy some food.")
	default:
		fmt.Println("No information available for that day.")
	}
}

二 循环语句

Go 只有一个循环语句,即 for 语句。基本的 for 循环结构如下

for init; condition; post {
	// 循环体执行的代码
}
  • init 是循环的初始化,只在进入循环时执行一次
  • condition 是条件表达式
    • conditiontrue 时,执行 for 中的代码
    • conditionfalse 时,结束循环
  • post 在每次迭代结束时执行,通常用于改变循环条件

init 语句通常是一个简短的变量声明,在那里声明的变量仅在 for 语句的作用域内可见。一旦condition 条件表达式的计算结果为 false ,循环将停止迭代

[!WARNING] 注意
forinit; condition; post 没有被 () 包围, {} 是必须的,且 { 必须和 for 在同一行

例如,计算 1 ∼ 100 1 \sim 100 1100 的和

package main

import "fmt"

func main() {
	// 基本结构
	sum := 0
	for j := 1; j <= 100; j++ {
		sum += j
	}
	fmt.Println(sum)
}

initpost 语句是可选的,它类似于 C 的 while 循环。在这一点上,你可以 去掉分号

for condition {}  // 类似 C 的 while

例如,计算 1 ∼ 100 1 \sim 100 1100 的和

package main

import "fmt"

func main() {
	// 省略 init 和 post 此时,类似 C 的 while,这样就可以省略两边的分号
	sum := 0
	i := 1
	for i <= 100 {
		sum += i
		i++
	}
	fmt.Println(sum)
}

如果省略循环条件,则它将永远循环,因此可以紧凑地表示无限循环。

for {}  // 类似于 C 的 for(;;)

如果 inti, condition, post 只省略其中之一,那么分号(;) 将不能省略

i := 0
for ; i < 10; i++ {
	// 条件
}

2.1 for 练习

2.1.1 输出 100 以内的奇数

package main  
  
import "fmt"  
  
func main() {  
    /* 找出 100 以内的奇数 */    fmt.Print("Even number: ")  
    for i := 0; i < 100; i++ {  
       if i%2 != 0 {  
          fmt.Print(i)  
       }  
       fmt.Print(" ")  
    }  
}

2.2.2 水仙花数

一个三位数的各位上的数的立方和等于该数本身则为水仙花数

  • 解决方法:找到数字的每个位上的数字,计算每个数字的立方,然后求和
package main

import (
	"fmt"
)

func main() {
	/* 一个三位数的各位上的数的立方和等于该数本身则为水仙花数:找出 100 ~ 999 内的所有水仙花数 */
	fmt.Print("daffodil number: ")
	for number := 100; number < 1000; number++ {
		hundred := number / 100  // 百位上的数字
		ten := number % 100 / 10 // 十位上的数字
		one := number % 10
		if hundred*hundred*hundred+ten*ten*ten+one*one*one == number {
			fmt.Print(number)
			fmt.Print(" ")
		}
	}
}

2.2.3 1 ∼ 100 1\sim 100 1100 内含 7 7 7 或是 7 7 7 的倍数的数字

package main

import (
	"fmt"
)

func main() {
	/* 1 ~ 100 含有 7 或是 7 的倍数的数 */
	fmt.Print("含有 7 或者 是 7 的倍数的数: ")
	for number := 7; number < 100; number++ {
		/*   7 的倍数         十位是 7          个位是 7 */
		if number%7 == 0 || number/10 == 7 || number%10 == 7 {
			fmt.Print(number)
			fmt.Print(" ")
		}
	}
}

2.3 range 关键字

关键 range 字用于更轻松地迭代数 数组、切片、字符串、map 及 通道(channel)

[!NOTE] 注意
数组,切片,map 马上就会涉及,但是通道将在 go coroutine 部分介绍

使用 range 关键字的方式如下

for index, value := array|slice|map {
   // 每次迭代执行的代码
}

鉴于我们只学习了 string,现在我们使用 range 关键字来遍历 string 类型的值

package main

import "fmt"

func main() {
	str := "你好, 小爱同学"
	for i, v := range str {
		//fmt.Printf("i type: %T, v type: %T\n", i, v) // v 的类型是 int32 这是由于 Go 使用 Unicode 处理字符串使用
		fmt.Println(i, string(v))
	}
}

Go 1.22 版本新增了 range 对整数的支持

for i := range 5 {
	fmt.Println(i)  // 0 1 2 3 4
}

for range 3 {
	fmt.Println("hello") // 输出 3 次 hello
}

2.4 continue 关键字

continue 语句用于跳过循环中的一个或多个迭代。然后,它继续进行循环中的下一次迭代

package main

import "fmt"

func main() {
	for i:=0; i < 10; i++ {
		if i % 2 != 0 {
			continue
		}
		fmt.Printf("%d ", i)
	}
}

continue语句后可以添加标签,表示开始标签对应的循环

package main

import "fmt"

func main() {
forloop1:
	for i := 0; i < 5; i++ {
		//forloop2:
		for j := 0; j < 5; j++ {
			if i == 2 || j == 2 {
				continue forloop1
			}
			fmt.Printf("%v-%v\n", i, j)
		}
	}
}

2.5 break 关键字

break 语句用于 中断/终止循环执行

package main

import "fmt"

func main() {
	for i:=0; i < 10; i++ {
		fmt.Printf("%d ", i)
		if i == 5 {
			break  // 跳出本层循环
		}
	}
	fmt.Printf("\n")
}

break语句还可以在语句后面添加 标签,表示 退出某个标签对应的代码块

package main

import "fmt"

func main() {
BREAKDEMO1:
	for i := 0; i < 10; i++ {
		for j := 0; j < 10; j++ {
			if j == 2 {
				break BREAKDEMO1 // 跳出标签指定的语句
			}
			fmt.Printf("%v-%v\n", i, j)
		}
	}
	fmt.Println("...")
}

break 关键字还可以用于 switchselect 的代码块上

2.6 嵌套的 for

可以将一个循环放在另一个循环中。处于另一个循环内部的循环称为 “内部循环”。在这里,“外部循环” 的每迭代执行一次,“内部循环” 将完整执行

示例:寻找 2 ∼ 100 2 \sim 100 2100 内的素数

package main

import "fmt"

func main() {
	/* 寻找 2 ~ 100 内的素数 */
	for num := 2; num < 100; num++ {
		var j int = 2
		for ; j < num; j++ {
			if num%j == 0 {
				fmt.Printf("%d is not a prime.\n", num)
				break // 跳出内层循环
			}
		}
		if j == num {
			fmt.Printf("%d is a prime.\n", num)
		}
	}
}

2.7 goto 关键字

goto 语句通过标签进行代码间的 无条件跳转goto 语句可以在快速跳出循环、避免重复退出上有一定的帮助。Go 语言中使用 goto 语句能简化一些代码的实现过程。 例如双层嵌套的 for 循环要退出时

func f() {
	a := 0
	if a == 1 {
		goto LABEL1
	} else {
		fmt.Println("other")
	}

LABEL1:
	fmt.Printf("next...")
}

func main() {
	f()
}
func f() {
	for i := 0; i < 5; i++ {
		for j := 0; j < 5; j++ {
			if i == 2 && j == 2 {
				goto LABEL1
			}
		}
	}
LABEL1:
	fmt.Println("label1")
}

func main() {
	f()
}

三 练习

3.1 判断一个整数是否为奇数或偶数

package main

import "fmt"

func main() {
	var num int
	fmt.Printf("请输入一个整数: ")
	fmt.Scanf("%d", &num)
	if num%2 == 0 {
		fmt.Printf("%d 是偶数\n", num)
	} else {
		fmt.Printf("%d 是奇数\n", num)
	}
}

3.2 计算并输出 1 1 1 100 100 100 之间所有能被 3 3 3 整除的数字的和

package main

import "fmt"

func main2() {
	sum := 0
	for i := 3; i < 100; i++ {
		if i%3 != 0 {
			continue
		}
		sum += i
	}
	fmt.Printf("1 到 100 中能被 3 整数的数字之和为: %d\n", sum)
}

3.3 九九乘法表:打印镜像

package main

import "fmt"

func main() {
	for i := 1; i < 10; i++ {
		for j := 1; j <= i; j++ {
			fmt.Printf("%d * %d = %-5d", i, j, i*j)
		}
		fmt.Println()
	}
	fmt.Println()
	for i := 9; i > 0; i-- {
		for j := 1; j <= i; j++ {
			fmt.Printf("%d * %d = %-5d", i, j, i*j)
		}
		fmt.Println()
	}
}

3.4 判断一个数是否为素数

package main

import (
	"fmt"
	"math"
)

func main() {
	var num int
	fmt.Scan(&num)

	var i int

	for i = 2; i < int(math.Sqrt(float64(num))); i++ {
		if num%i == 0 {
			break
		}
	}
	if i < int(math.Sqrt(float64(num))) {
		fmt.Printf("%d 不是一个素数\n", num)
	} else {
		fmt.Printf("%d 是一个素数\n", num)
	}
}

3.5 回文串判断

package main

import "fmt"

func main() {
	var str string
	fmt.Printf("输入一个字符串: ")
	fmt.Scan(&str)

	// var i, j = 0, len(str) - 1   len 返回的不是字符数而是字节数
	rune_string := []rune(str)
	i, j := 0, len(rune_string)-1
	for i != j {
		if rune_string[i] != rune_string[j] {
			break
		}
		i++
		j--
	}
	if i == j {
		fmt.Printf("%s 是一个回文串\n", str)
	} else {
		fmt.Printf("%s 不是一个回文串\n", str)
	}
}

3.6 计算并输出 n n n 的阶乘

package main

import "fmt"

func main() {
	var num int
	fmt.Printf("输入一个数: ")
	fmt.Scan(&num)
	fac := 1
	for i := 1; i <= num; i++ {
		fac *= i
	}
	fmt.Printf("%d 的阶乘为: %d\n", num, fac)
}

计算并输出斐波拉契数列的第 n n n

package main

import "fmt"

func main() {
	var num int
	fmt.Printf("输入整数: ")
	fmt.Scan(&num)
	a, b := 0, 1
	for num > 0 {
		a, b = b, a+b
		num--
	}
	fmt.Printf("第 %d 项斐波拉契数列为: %d\n", num, a)
}

总结

本节我们介绍了 Go 中的控制结构。

显示全文