1.go语言——switc语句
switch语句提供了一个多分支条件执行的方法。不过在这里用一个专有名词来代表分支——case。每一个case可以携带一个表达式或一个类型说明符。前者又可被简称为case表达式。因此,Go语言的switch语句又分为表达式switch语句和类型switch语句。
在表达式switch语句中,每个case会携带一个表达式。与if语句中的条件表达式不同,这里的case表达式的结果类型并不一定是bool。不过,它们的结果类型需要与switch表达式的结果类型一致。所谓switch表达式是指switch语句中要被判定的那个表达式。switch语句会依据该表达式的结果与各个case表达式的结果是否相同来决定执行哪个分支。
var name string
// 省略若干条语句
switch name {
case "Golang":
fmt.Println("A programming language from Google.")
case "Rust":
fmt.Println("A programming language from Mozilla.")
default:
fmt.Println("Unknown!")
}
可以看到,在上述switch语句中,name充当了switch表达式,而"Go"和"Rust"充当了case表达式。它们的结果类型是一致的,都是string。顺便说一句,可以有只包含一个字面量或标识符的表达式。它们是最简单的表达式,属于基本表达式的一种。
switch表达式必须紧随switch关键字出现。在后面的花括号中,一个关键字case、case表达式、冒号以及后跟的若干条语句组成为一条case语句。在switch语句中可以有若干条case语句。Go语言会依照从上至下的顺序对每一条case语句中case表达式进行求值。只要被发现其表达式与switch表达式的结果相同,该case语句就会被选中。它包含的那些语句就会被执行。而其余的case语句则会被忽略。
switch语句中还可以存在一个特殊的case——default case。顾名思义,当没有一个常规的case被选中的时候,default case就会被选中。上面示例中就存在一个default case。它由关键字default、冒号和后跟的一条语句组成。实际上,default case不一定被追加在最后。它可以是第一个case,或者出现在任意位置上。
另外,与if语句一样,switch语句还可以包含初始化子句,且其出现位置和写法也如出一辙。如:
names := []string{"Golang", "Java", "Rust", "C"}
switch name := names[0]; name {
case "Golang":
fmt.Println("A programming language from Google.")
case "Rust":
fmt.Println("A programming language from Mozilla.")
default:
fmt.Println("Unknown!")
}
类型switch语句,它与一般形式有两点差别。第一点,紧随case关键字的不是表达式,而是类型说明符。类型说明符由若干个类型字面量组成,且多个类型字面量之间由英文逗号分隔。第二点,它的switch表达式是非常特殊的。这种特殊的表达式也起到了类型断言的作用,但其表现形式很特殊,如:
v.(type),其中v必须代表一个接口类型的值。注意,该类表达式只能出现在类型switch语句中,且只能充当switch表达式。
一个类型switch语句的示例如下:
v := 11
switch i := interface{}(v).(type) {
case int, int8, int16, int32, int64:
fmt.Printf("A signed integer: %d. The type is %T. \n", i, i)
case uint, uint8, uint16, uint32, uint64:
fmt.Printf("A unsigned integer: %d. The type is %T. \n", i, i)
default:
fmt.Println("Unknown!")
}
请注意,我们在这里把switch表达式的结果赋给了一个变量。如此一来,我们就可以在该switch语句中使用这个结果了。这段代码被执行后,标准输出上会打印出A signed integer: 11. The type is int.。
最后,我们来说一下fallthrough。它既是一个关键字,又可以代表一条语句。fallthrough语句可被包含在表达式switch语句中的case语句中。它的作用是使控制权流转到下一个case。不过要注意,fallthrough语句仅能作为case语句中的最后一条语句出现。并且,包含它的case语句不能是其所属switch语句的最后一条case语句。
实例代码:
package main
import (
"fmt"
"math/rand"
)
func main() {
ia := []interface{}{byte(6), 'a', uint(10), int32(-4)}
switch v := ia[rand.Intn(4):4]; interface{}(v).(type) {
case []interface{}:
fmt.Println("类型switch语句")
case byte:
fmt.Println("数据类型为byte")
default:
fmt.Println("如果前面条件不满足将会执行这条")
}
age := 40
switch age {
case 10:
fmt.Println("小孩")
case 20:
fmt.Println("少年")
case 30:
fmt.Println("青年")
case 40:
fmt.Println("中年")
fallthrough
case 50:
fmt.Println("中年后")
case 60:
fmt.Println("古稀")
default:
fmt.Println("不是人类")
}
}
2.go语言——for语句
for语句代表着循环。一条语句通常由关键字for、初始化子句、条件表达式、后置子句和以花括号包裹的代码块组成。其中,初始化子句、条件表达式和后置子句之间需用分号分隔。
示例如下:
for i := 0; i < 10; i++ {
fmt.Print(i, " ")
}
我们可以省略掉初始化子句、条件表达式、后置子句中的任何一个或多个,不过起到分隔作用的分号一般需要被保留下来,除非在仅有条件表达式或三者全被省略时分号才可以被一同省略。
我们可以把上述的初始化子句、条件表达式、后置子句合称为for子句。实际上,for语句还有另外一种编写方式,那就是用range子句替换掉for子句。range子句包含一个或两个迭代变量(用于与迭代出的值绑定)、特殊标记
:=或=、关键字range以及range表达式。其中,range表达式的结果值的类型应该是能够被迭代的,包括:字符串类型、数组类型、数组的指针类型、切片类型、字典类型和通道类型。
例如:
for i, v := range "Go语言" {
fmt.Printf("%d: %c\n", i, v)
}
对于字符串类型的被迭代值来说,for语句每次会迭代出两个值。第一个值代表第二个值在字符串中的索引,而第二个值则代表该字符串中的某一个字符。迭代是以索引递增的顺序进行的。例如,上面的for语句被执行后会在标准输出上打印出:
0: G
1: o
2: 语
5: 言
可以看到,这里迭代出的索引值并不是连续的。下面我们简单剖析一下此表象的本质。我们知道,字符串的底层是以字节数组的形式存储的。而在Go语言中,字符串到字节数组的转换是通过对其中的每个字符进行UTF-8编码来完成的。字符串"Go语言"中的每一个字符与相应的字节数组之间的对应关系如下:
注意,一个中文字符在经过UTF-8编码之后会表现为三个字节。
所以,我们用语[0]、语[1]和、语[2]分别表示字符’语’经编码后的第一、二、三个字节。对于字符’言’,我们如法炮制。
对照这张表格,我们就能够解释上面那条for语句打印出的内容了,即:每次迭代出的第一个值所代表的是第二个字符值经编码后的第一个字节在该字符串经编码后的字节数组中的索引值。
对于数组值、数组的指针值和切片之来说,range子句每次也会迭代出两个值。其中,第一个值会是第二个值在被迭代值中的索引,而第二个值则是被迭代值中的某一个元素。同样的,迭代是以索引递增的顺序进行的。
对于字典值来说,range子句每次仍然会迭代出两个值。显然,第一个值是字典中的某一个键,而第二个值则是该键对应的那个值。注意,对字典值上的迭代,Go语言是不保证其顺序的。
携带range子句的for语句还可以应用于一个通道值之上。其作用是不断地从该通道值中接收数据,不过每次只会接收一个值。注意,如果通道值中没有数据,那么for语句的执行会处于阻塞状态。无论怎样,这样的循环会一直进行下去。直至该通道值被关闭,for语句的执行才会结束。
break语句和continue语句都可以被放置在for语句的代码块中。前者被执行时会使其所属的for语句的执行立即结束,而后者被执行时会使当次迭代被中止(当次迭代的后续语句会被忽略)而直接进入到下一次迭代。
实例一:
package main
//go语言嵌套循环
import "fmt"
func main() {
//定义局部变量
var i, j int
for i = 2; i < 100; i++ {
for j = 2; j <= (i / j); j++ {
if i%j == 0 {
break //如果发现因子,则不是素数
}
}
if j > (i / j) {
fmt.Printf("%d 是素数\n", i)
}
}
}
实例二:
package main
import "fmt"
func main() {
map1 := map[int]string{1: "Golang", 2: "Java", 3: "go", 4: "python", 5: "Html5"}
for key, value := range map1 {
fmt.Printf("%d: %s\n", key, value)
}
fmt.Printf("\n\n")
for _, v := range []int{1, 2, 3, 4, 5, 6, 7, 8, 9} {
if v == 5 {
continue
}
fmt.Printf("%d: %s\n", v, map1[v])
}
fmt.Printf("\n\n")
for i := 10; i >= 0; i -= 2 {
if i == 4 {
break
}
fmt.Printf("%d\n", i)
}
fmt.Printf("\n\n")
for j := 1; j <= len(map1); j++ {
fmt.Printf("%d: %s\n", j, map1[j])
}
}
3.go语言——select语句
select语句属于条件分支流程控制方法,不过它只能用于通道。它可以包含若干条case语句,并根据条件选择其中的一个执行。进一步说,select语句中的case关键字只能后跟用于通道的发送操作的表达式以及接收操作的表达式或语句。
示例如下:
ch1 := make(chan int, 1)
ch2 := make(chan int, 1)
// 省略若干条语句
select {
case e1 := <-ch1:
fmt.Printf("1th case is selected. e1=%v.\n", e1)
case e2 := <-ch2:
fmt.Printf("2th case is selected. e2=%v.\n", e2)
default:
fmt.Println("No data!")
}
如果该select语句被执行时通道ch1和ch2中都没有任何数据,那么肯定只有default case会被执行。但是,只要有一个通道在当时有数据就不会轮到default case执行了。显然,对于包含通道接收操作的case来讲,其执行条件就是通道中存在数据(或者说通道未空)。如果在当时有数据的通道多于一个,那么Go语言会通过一种伪随机的算法来决定哪一个case将被执行。
另一方面,对于包含通道发送操作的case来讲,其执行条件就是通道中至少还能缓冲一个数据(或者说通道未满)。类似的,当有多个case中的通道未满时,它们会被随机选择。请看下面的示例:
ch3 := make(chan int, 100)
// 省略若干条语句
select {
case ch3 <- 1:
fmt.Printf("Sent %d\n", 1)
case ch3 <- 2:
fmt.Printf("Sent %d\n", 2)
default:
fmt.Println("Full channel!")
}
该条select语句的两个case中包含的都是针对通道ch3的发送操作。如果我们把这条语句置于一个循环中,那么就相当于用有限范围的随机整数集合去填满一个通道。
请注意,如果一条select语句中不存在default case, 并且在被执行时其中的所有case都不满足执行条件,那么它的执行将会被阻塞!当前流程的进行也会因此而停滞。直到其中一个case满足了执行条件,执行才会继续。我们一直在说case执行条件的满足与否取决于其操作的通道在当时的状态。这里特别强调一点,即:未被初始化的通道会使操作它的case永远满足不了执行条件。对于针对它的发送操作和接收操作来说都是如此。
break语句也可以被包含在select语句中的case语句中。它的作用是立即结束当前的select语句的执行,不论其所属的case语句中是否还有未被执行的语句。
实例代码:
package main
import "fmt"
func main() {
//创建一个长度为1,类型为int的通道
ch := make(chan int, 1)
for i := 0; i < 4; i++ {
select {
case e, ok := <-ch:
if !ok {
fmt.Println("End.")
return
}
fmt.Println(e)
close(ch) //关闭通道
default:
fmt.Println("No Data!")
ch <- 1
}
}
}
好了,分享就到这里,后续还会不断更新,感谢各位的支持!
共同学习,写下你的评论
评论加载中...
作者其他优质文章