第十篇 Switch 语句

欢迎来到Golang教程系列的第十篇

什么是switch语句

switch是条件语句,判断一个表达式,比较列出的所有可能的匹配值,然后执行相关的代码块。可以考虑用这个来代替复杂的if else从句。

例子

一个好的例子比说一百句话都管用,让我们写一个简单例子。就输入一个手指数,然后输出这个手指的名称,例如,1是拇指,2是食指,诸如此类。

package main

import (
	"fmt"
)

func main() {

	finger := 4
	fmt.Printf("Finger %d is ", finger)
	switch finger {
	case 1:
		fmt.Println("拇指")
	case 2:
		fmt.Println("食指")
	case 3:
		fmt.Println("中指")
	case 4:
		fmt.Println("无名指")
	case 5:
		fmt.Println("小指")
	}
}

在上面的程序中,switch finger语句, finger的值和所有case语句的值做比较,顺序从上到下,第一个匹配的表达是就会执行,在上面的例子中,finger的值为4,所以会打印出

Finger 4 is 无名指

重复的case是不允许的

有多个重复值的case是不允许的,如果你尝试运行下面的程序,编译器就会报错./main.go:20:7: duplicate case 4 in switch previous case at ./main.go:18:7

package main

import (
	"fmt"
)

func main() {

	finger := 4
	fmt.Printf("Finger %d is ", finger)
	switch finger {
	case 1:
		fmt.Println("拇指")
	case 2:
		fmt.Println("食指")
	case 3:
		fmt.Println("中指")
	case 4:
		fmt.Println("无名指")
	case 4:  // 重复的值
		fmt.Println("另外一个无名指")
	case 5:
		fmt.Println("小指")
	}
}

默认case

我们一只手只有五个手指,如果我们输入了一个错误的手指数会怎么样呢?这时候默认(defult)就闪亮登场了,默认case会在其它case没有匹配的时候执行。

package main

import (
	"fmt"
)

func main() {

	switch finger := 8; finger {
	case 1:
		fmt.Println("拇指")
	case 2:
		fmt.Println("食指")
	case 3:
		fmt.Println("中指")
	case 4:
		fmt.Println("无名指")
	case 5:
		fmt.Println("小指")
	default:  //默认case
		fmt.Println("错误的手指数")
	}
}

上面的程序中,finger为8,并没有匹配任何的case,所以会打印出错误的手指数,defaultcase并没有要求说要放在switch语句的最后面,它可以放在switch的任何地方。

你可能也留意到了关于声明finger有个小改变,它是在switch语句中声明。在执行判断表达式之前,switch还包含一个可选的语句。上面的例子中,finger首先声明,然后才执行表达式,finger的作用范围是在switch模块内。

case里面多个表达式

在一个case里面可以放多个表达式,只需用逗号隔开。

package main

import (
	"fmt"
)

func main() {

	letter := "i"
	fmt.Printf("Letter %s is a ", letter)
	switch letter {
	case "a", "e", "i", "o", "u":  //case里有多个表达式
		fmt.Println("vowel")
	default:
		fmt.Println("not a vowel")
	}

}

上面的程序判断letter是不是元音字母(vowel),case "a", "e", "i", "o", "u":匹配了所有的元音字母,因为i是一个元音字母,所有程序将会输出

Letter i is a vowel

无表达式的switch

表达式在switch中是可选的,可以省略的。如果省略了表达式,那么switch就会默认为switch true,然后判断每个case表达式是否为truth,然后执行相关的代码块。

package main

import (
	"fmt"
)

func main() {

	num := 75
	switch {  //省略了表达式 
	case num >= 0 && num <= 50:
		fmt.Printf("%d大于0,小于50", num)
	case num >= 50 && num <= 100:
		fmt.Printf("%d大于51,小于100",num)
	case num >= 101:
		fmt.Printf("%d大于100",num)
	}

}

上面的程序中,switch是省略了的,因此默认为true,并且判断case表达式是否为true。而num >= 50 && num <= 100:true,所以程序会输出:

75大于51,小于100

这个类型的switch可以考虑用来代替多个if else从句。

Fallthrough 失败?

在Go中,一个case语句执行以后,会马上跳出switch语句。fallthrough语句可以传输控制当一个case执行后,紧接着执行下一个的case。

让我们写一个程序来了解下fallthrough。我们的程序会检查输入数字是否小于50,100,200。例如,如果我们输入75,那么程序就会打印100和200,我们将通过fallthrough来实现。

package main

import (
	"fmt"
)

func number() int {
	num := 15 * 5
	return num
}

func main() {

	switch num := number(); {   // num不是一个常量
	case num < 50:
		fmt.Printf("%d小于50\n",num)
		fallthrough
	case num < 100:
		fmt.Printf("%d小于100\n",num)
		fallthrough
	case num < 200:
		fmt.Printf("%d小于200",num)
	}

}

switch和case表达式不一定是要常量,它们也可以在运行时求值。在上面的程序中,num初始值为函数number()的返回值。控制流来到case num < 100:判断为true,所以程序会执行输出75小于100,下一个语句是fallthrough。当遇到fallthrough时,控制流会转移到紧接着的下一个case语句,并打印出75小于200。下面是程序的输出。

75小于100
75小于200

fallthrough应该放在case语句的最后面,如果放在了其它地方,编译器就会报错fallthrough statement out of place

当Fallthrough遇到case判断失败时

使用fallthrough时还有一个需要考虑的,就是执行的下一个case,即使case表达式是false的,case内容也会执行。

思考下下面程序的输出。

package main

import (
	"fmt"
)

func main() {

	switch num := 25; {
	case num < 50:
		fmt.Printf("%d 小于50\n",num)
		fallthrough
	case num > 100:
		fmt.Printf("%d 大于100\n".num)
	}
	
}

在上面的程序中,num值为25,小于50, 因此case num < 50计算值为true,接着出现fallthrough。转而执行下一个case,表达式为case num > 100:,因为num<100,所以值为false。 但是fallthrough不考虑这个,即使它的下一个case为false也会执行。

上面的程序会输出

25 is lesser than 50  
25 is greater than 100 

所以你要确保自己知道fallthrough用在哪个场景。

还有一点是,fallthrough不能放在switch的最后一个case,因为在这之后,没有其它的case了,倘若你这样做了,那将会编译报错。

cannot fallthrough final case in switch

Breaking switch

break可以用来在switch自己完成之前提前终止它。让我们修改下上面的程序,理解下break是怎么工作的。

让我们添加一个条件判断num小于0,然后终止switch。

package main

import (
	"fmt"
)

func main() {

	switch num := -5; {
	case num < 50:
		if num < 0 {
			break
		} 
		fmt.Printf("%d 小于50\n",num)
		fallthrough
	case num > 100:
		fmt.Printf("%d 大于100\n",num)
		fallthrough
	case num < 200:
		fmt.Printf("%d 大于200\n",num)
	}

}

在上面的程序中,num-5,当运行控制到if num < 0语句时,条件语句满足num < 0,break语句会在自动完成之前提前终止,不会再打印任何东西。

Breaking 外层loop循环

当switch语句放在一个for循环里面时,有可能需要会需要提前终止for循环.我们可以通过for循环标记来实现,breaking for循环需要把标记放在switch语句里面。让我们来看下例子。

我们要写一个生成随机数的程序。

我们会先生成一个无限的for循环,然后用switch case判断生成的随机数是否为偶数,如果是偶数,那就打印出生成的数字,并且使用标记终止for循环。intn函数和rand包是用来生成非负的伪随机数。

package main

import (
	"fmt"
	"math/rand"
)

func main() {

randloop:
	for {
		switch i := rand.Intn(100); {
		case i%2 == 0:
			fmt.Printf("Generated even num %d", i)
			break randloop
		}
	}
}

在上面的程序中,for循环的标记为randloop,使用Intn函数生成从0到99的随机数。如果生成的随机数是偶数,那么就会使用标记(label)来终止程序。

上面程序会打印

Generated even num 18

请注意,如果使用break语句时没有带上标记label,那只会break switch语句,for循环还会继续运行。所以要break外层for循环时,标记循环并且在switch内部break标记是必不可少的。

以上就是本篇的全部,感谢阅读,这是我第一次翻译,难免会有翻译不当的地方,如果有什么反馈和评论,欢迎提出来!

原文地址: https://golangbot.com/switch/