Go-流程控制
[TOC]
if 判断语句
if 100 > number {
number += 3
} else if 100 < number {
number -= 2
} else {
fmt.Println("OK!")
}
//变量赋值
if number := 4; 100 > number {
number += 3
}
// 标识符的重声明和标识符的遮蔽
var number int
if number := 4; 100 > number {
number += 3
}
// 或条件
if (width == 0) ||(height == 0) || (offsetX == 0 )|| (offsetY == 0) {
return nil
}
标识符的重声明和标识符的遮蔽
上述代码被执行完毕之后,第二次声明的number
变量的值会是7
,而第一次声明的number
变量的值仍会是0
。
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!")
}
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!")
}
for 循环语句
for i := 0; i < 10; i++ {
fmt.Print(i, " ")
}
for i, v := range "Go语言" {
fmt.Printf("%d: %c\n", i, v)
}
/* print
0: G
1: o
2: 语
5: 言
*/
// 注意,一个中文字符在经过UTF-8编码之后会表现为三个字节。
/*
range表达式的结果值的类型应该是能够被迭代的,包括:
字符串类型、
数组类型、
数组的指针类型、
切片类型、
字典类型
通道类型
for语句每次会迭代出两个值。
第一个值代表第二个值在字符串中的索引,
第二个值则代表该字符串中的某一个字符。
*/
最后,我们来说一下break
语句和continue
语句。它们都可以被放置在for
语句的代码块中。break被执行时会使其所属的for
语句的执行立即结束,continue被执行时会使当次迭代被中止(当次迭代的后续语句会被忽略)而直接进入到下一次迭代。
for中变量是对外部变量的copy,如何操作外部变量呢?
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
永远满足不了执行条件。对于针对它的发送操作和接收操作来说都是如此。
ch4 := make(chan int, 1)
for i := 0; i < 4; i++ {
select {
case e, ok := <-ch4:
if !ok {
fmt.Println("End.")
return
}
fmt.Println(e)
close(ch4)
default:
fmt.Println("No Data!")
ch4 <- 1
}
}
defer[dɪˈfɚ] 推迟语句
defer
语句仅能被放置在函数或方法中。它由关键字defer
和一个调用表达式组成。注意,这里的调用表达式所表示的既不能是对Go语言内建函数的调用也不能是对Go语言标准库代码包unsafe
中的那些函数的调用。
func readFile(path string) ([]byte, error) {
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer file.Close()
return ioutil.ReadAll(file)
}
注意,当这条defer
语句被执行的时候,其中的这条表达式语句并不会被立即执行。它的确切的执行时机是在其所属的函数(这里是readFile
)的执行即将结束的那个时刻。也就是说,在readFile
函数真正结束执行的前一刻,file.Close()
才会被执行。
注意,当一个函数中存在多个defer
语句时,它们携带的表达式语句的执行顺序一定是它们的出现顺序的倒序。
最后,对于defer
语句,我还有两个特别提示:
defer
携带的表达式语句代表的是对某个函数或方法的调用。这个调用可能会有参数传入,比如:fmt.Print(i + 1)
。如果代表传入参数的是一个表达式,那么在defer
语句被执行的时候该表达式就会被求值了。注意,这与被携带的表达式语句的执行时机是不同的。如果defer携带的表达式语句代表的是对匿名函数的调用,那么我们就一定要非常警惕。正确的用法是:把要使用的外部变量作为参数传入到匿名函数中。
func deferIt4() { for i := 1; i < 5; i++ { defer func(n int) { fmt.Print(n) }(i) } }