介绍
条件语句使程序员能够指导他们的程序在条件为真时采取一些行动,而在条件为假时采取另一种行动。 通常,我们希望将一些 变量 与多个可能的值进行比较,在每种情况下采取不同的操作。 可以单独使用 if 语句 来完成这项工作。 然而,编写软件不仅是为了让事情顺利进行,而且还要将你的意图传达给你未来的自己和其他开发人员。 switch
是另一种条件语句,可用于传达 Go 程序在呈现不同选项时所采取的操作。
我们可以用 switch 语句编写的所有内容也可以用 if
语句编写。 在本教程中,我们将看一些关于 switch 语句可以做什么的示例,它替换的 if
语句,以及它最适合应用的地方。
Switch 语句的结构
Switch 通常用于描述程序在为变量分配特定值时所采取的动作。 以下示例演示了我们如何使用 if
语句来完成此操作:
package main import "fmt" func main() { flavors := []string{"chocolate", "vanilla", "strawberry", "banana"} for _, flav := range flavors { if flav == "strawberry" { fmt.Println(flav, "is my favorite!") continue } if flav == "vanilla" { fmt.Println(flav, "is great!") continue } if flav == "chocolate" { fmt.Println(flav, "is great!") continue } fmt.Println("I've never tried", flav, "before") } }
这将生成以下输出:
Outputchocolate is great! vanilla is great! strawberry is my favorite! I've never tried banana before
在 main
中,我们定义了冰淇淋口味的 slice。 然后我们使用 for 循环 来遍历它们。 我们使用三个 if
语句打印出不同的消息,表明对不同冰淇淋口味的偏好。 每个 if
语句必须使用 continue
语句来停止 for
循环的执行,以便不会为首选冰淇淋口味打印末尾的默认消息。
当我们添加新的冰淇淋偏好时,我们必须不断添加 if
语句来处理新的情况。 重复的消息,如 "vanilla"
和 "chocolate"
的情况,必须有重复的 if
语句。 对于我们代码的未来读者(包括我们自己)来说,if
语句的重复性掩盖了它们所做工作的重要部分——将变量与多个值进行比较并采取不同的操作。 此外,我们的后备消息与条件语句分开,使其看起来不相关。 switch
语句可以帮助我们更好地组织这个逻辑。
switch
语句以 switch
关键字开头,然后以最基本的形式跟一些变量进行比较。 后面是一对大括号 ({}
),其中可以出现多个 case 子句。 Case 子句描述了当提供给 switch 语句的变量等于 case 子句引用的值时你的 Go 程序应该采取的动作。 以下示例将前面的示例转换为使用 switch
而不是多个 if
语句:
package main import "fmt" func main() { flavors := []string{"chocolate", "vanilla", "strawberry", "banana"} for _, flav := range flavors { switch flav { case "strawberry": fmt.Println(flav, "is my favorite!") case "vanilla", "chocolate": fmt.Println(flav, "is great!") default: fmt.Println("I've never tried", flav, "before") } } }
输出与之前相同:
Outputchocolate is great! vanilla is great! strawberry is my favorite! I've never tried banana before
我们再次在 main
中定义了一片冰淇淋口味,并使用 range
语句迭代每种口味。 然而,这一次,我们使用了 switch
语句来检查 flav
变量。 我们使用两个 case
子句来表示偏好。 我们不再需要 continue
语句,因为 switch
语句只会执行一个 case
子句。 我们还可以通过在 case
子句的声明中用逗号分隔每个条件来组合 "chocolate"
和 "vanilla"
条件的重复逻辑。 default
子句是我们的包罗万象的子句。 它将适用于我们在 switch
语句的主体中没有考虑的任何风格。 在这种情况下,"banana"
将导致 default
执行,打印消息 I've never tried banana before
。
switch
语句的这种简化形式解决了它们最常见的用途:将一个变量与多个备选方案进行比较。 它还为我们提供了便利,我们希望通过使用提供的 default
关键字对多个不同的值执行相同的操作以及在没有满足列出的条件时执行一些其他操作。
当 switch
的这种简化形式证明过于局限时,我们可以使用 switch
语句的更一般形式。
通用切换语句
switch
语句对于将更复杂的条件集合进行分组以显示它们以某种方式相关时很有用。 这在将某个变量与一系列值进行比较时最常用,而不是前面示例中的特定值。 以下示例使用可以从 switch
语句中受益的 if
语句实现猜谜游戏:
package main import ( "fmt" "math/rand" "time" ) func main() { rand.Seed(time.Now().UnixNano()) target := rand.Intn(100) for { var guess int fmt.Print("Enter a guess: ") _, err := fmt.Scanf("%d", &guess) if err != nil { fmt.Println("Invalid guess: err:", err) continue } if guess > target { fmt.Println("Too high!") continue } if guess < target { fmt.Println("Too low!") continue } fmt.Println("You win!") break } }
输出将根据选择的随机数和您玩游戏的程度而有所不同。 以下是一个示例会话的输出:
OutputEnter a guess: 10 Too low! Enter a guess: 15 Too low! Enter a guess: 18 Too high! Enter a guess: 17 You win!
我们的猜谜游戏需要一个随机数来比较猜测,所以我们使用 math/rand
包中的 rand.Intn
函数。 为了确保每次玩游戏时我们得到不同的 target
值,我们使用 rand.Seed
根据当前时间随机化随机数生成器。 100
到 rand.Intn
的参数将给我们一个 0-100 范围内的数字。 然后我们使用 for
循环开始收集玩家的猜测。
fmt.Scanf
函数为我们提供了一种将用户输入读入我们选择的变量的方法。 它采用格式字符串动词,将用户的输入转换为我们期望的类型。 这里的 %d
意味着我们期望 int
,并且我们传递 guess
变量的地址,以便 fmt.Scanf
能够设置该变量。 在 处理任何解析错误 之后,我们使用两个 if
语句将用户的猜测与 target
值进行比较。 它们返回的 string
与 bool
一起控制向玩家显示的消息以及游戏是否退出。
这些 if
语句掩盖了一个事实,即与变量进行比较的值范围都以某种方式相关。 也很难一目了然地判断我们是否错过了范围的某些部分。 下一个示例重构了前面的示例,改为使用 switch
语句:
package main import ( "fmt" "math/rand" ) func main() { target := rand.Intn(100) for { var guess int fmt.Print("Enter a guess: ") _, err := fmt.Scanf("%d", &guess) if err != nil { fmt.Println("Invalid guess: err:", err) continue } switch { case guess > target: fmt.Println("Too high!") case guess < target: fmt.Println("Too low!") default: fmt.Println("You win!") return } } }
这将生成类似于以下内容的输出:
OutputEnter a guess: 25 Too low! Enter a guess: 28 Too high! Enter a guess: 27 You win!
在这个版本的猜谜游戏中,我们将 if
语句块替换为 switch
语句。 我们省略了 switch
的表达式参数,因为我们只对使用 switch
来收集条件感兴趣。 每个 case
子句都包含一个比较 guess
和 target
的不同表达式。 与我们第一次用 switch
替换 if
语句类似,我们不再需要 continue
语句,因为只会执行一个 case
子句。 最后,default
子句处理 guess == target
的情况,因为我们已经用其他两个 case
子句覆盖了所有其他可能的值。
在我们目前看到的例子中,只有一个 case 语句会被执行。 有时,您可能希望组合多个 case
子句的行为。 switch
语句为实现此行为提供了另一个关键字。
穿越
有时您会想要重用另一个 case
子句包含的代码。 在这些情况下,可以让 Go 运行使用 fallthrough
关键字列出的下一个 case
子句的主体。 下一个示例修改了我们之前的冰淇淋风味示例,以更准确地反映我们对草莓冰淇淋的热情:
package main import "fmt" func main() { flavors := []string{"chocolate", "vanilla", "strawberry", "banana"} for _, flav := range flavors { switch flav { case "strawberry": fmt.Println(flav, "is my favorite!") fallthrough case "vanilla", "chocolate": fmt.Println(flav, "is great!") default: fmt.Println("I've never tried", flav, "before") } } }
我们将看到这个输出:
Outputchocolate is great! vanilla is great! strawberry is my favorite! strawberry is great! I've never tried banana before
正如我们之前看到的,我们定义了一个 string
切片来表示风味,并使用 for
循环遍历它。 此处的 switch
语句与我们之前看到的相同,但在 case
子句的末尾添加了 fallthrough
关键字,用于 [ X162X]。 这将导致 Go 运行 case "strawberry":
的主体,首先打印出字符串 strawberry is my favorite!
。 当它遇到 fallthrough
时,它将运行下一个 case
子句的主体。 这将导致 case "vanilla", "chocolate":
的主体运行,打印 strawberry is great!
。
Go 开发人员不经常使用 fallthrough
关键字。 通常,使用fallthrough
实现的代码复用,可以通过用通用代码定义一个函数来更好的获得。 由于这些原因,通常不鼓励使用 fallthrough
。
结论
switch
语句帮助我们向阅读我们代码的其他开发人员传达一组比较以某种方式相互关联。 它们使将来添加新案例时添加不同的行为变得更加容易,并且可以确保使用 default
子句正确处理我们忘记的任何内容。 下次当您发现自己编写多个涉及相同变量的 if
语句时,请尝试使用 switch
语句重写它——您会发现在需要考虑一些其他替代值。
如果您想了解有关 Go 编程语言的更多信息,请查看整个 如何在 Go 中编写代码系列 。