文章目录
- For
- for [( init; condition; increment )]{}
- for [condition]{}
- for [Range]{}
- 注意 Range 循环的对象是引用类型还是值类型!!!
- Switch
- 通过 switch 的值和 case 的值是否一致,判断逻辑分支
- 省略条件表达式
- Type Switch 判断接口类型
- Select
For
Go 中的 For 循环可以分为三种形式:
for [( init; condition; increment )]{}
示例
for i := 0; i < 10; i++ {fmt.Println(i)
}
for [condition]{}
直接判断条件是否成立,相当于 while(condition)
i := 0
for i < 10 {fmt.Println(i)i++
}
当然我们可以省略关系表达式,那样就变成了一个无限循环 while(true)
for {fmt.Println("circle")
}
for [Range]{}
for range 是比较常用的循环手段,我们可以通过 range 来获取集合对象中元素的下标和值
其中 i 为元素下标,v 为元素值
list := []string{"one", "two", "three"}
for i, v := range list {fmt.Println(i, v)
}
-----------------------------
0 one
1 two
2 three
如果我们在 range 的左侧只有一个接收值,那么这个值表示元素下标
list := []string{"one", "two", "three"}
for i := range list {fmt.Println(i)
}
-----------------------------
0
1
2
注意 Range 循环的对象是引用类型还是值类型!!!
如下代码:
一个是 数组 list 一个是 切片 list_slice ,拥有同样的元素。
我们从第二个元素开始,将当前元素加上前一个元素的值作为新的值。
最后查看打印结果,两者却不一样,切片打印结果(一直累加)符合我们的预期。
list := [...]int{1, 1, 1, 1, 1}
for i, v := range list {if i+1 < len(list) {list[i+1] += v}
}
fmt.Println(list)list_slice := []int{1, 1, 1, 1, 1}
for i, v := range list_slice {if i+1 < len(list_slice) {list_slice[i+1] += v}
}
fmt.Println(list_slice)
-----------------------------------
[1 2 2 2 2]
[1 2 3 4 5]
原因如下:
首先,切片是引用类型的,数组是值类型的。
其次,循环伊始的 Range 表达式,只会在 for 语句执行时被求值一次,即,当运行 for i, v := range list 这一句之后,v 的值就已经被确认了。而且在数组循环中,v 的值其实只是一个副本,对原来值的修改不会影响到 v 的值。而对于引用类型的切片来说,其在循环时使用的副本实际上是指针的副本,副本指针指向的内存地址和原指针指向的内存地址是一样的,我们对 v 的修改就是对这个内存地址的值做的修改。
当然我们可以不在 Range 阶段求值,那样就会避免求值产生副本的问题。
list := [...]int{1, 1, 1, 1, 1}
for i := range list {if i+1 < len(list) {list[i+1] += list[i]}
}
fmt.Println(list)
-----------------------------------
[1 2 3 4 5]
Switch
在 switch 语句中,每一个 case 分支都是唯一的,从上直下逐一测试,直到匹配为止。 switch 分支表达式可以是任意类型,不限于常量。可省略 break,默认自动终止。
通过 switch 的值和 case 的值是否一致,判断逻辑分支
case 表达式中,可以放多个情况值。
Go 语言中每个分支的最后默认都是有一个 break 的,使用 fallthrough 可以在走到当前分支后强制执行下一个 case 。
var ilist = []uint8{0, 1, 2, 3}
switch ilist[0] {
case 0:fmt.Println("000")
case 1:fmt.Println("111222")fallthrough
default:fmt.Println("default")
}
fmt.Println("--------分割线--------")
//使用 fallthrough 可以在走到当前分支后强制执行下一个 case
switch ilist[2] {
case 0:fmt.Println("000")
case 1 , 2:fmt.Println("111222")fallthrough
default:fmt.Println("default")
}
----------------------------------
000
--------分割线--------
111222
default
省略条件表达式
也可以省略条件表达式,当作 if… else… 使用
var i int = 1
switch {
case i > 0 && i < 2:fmt.Println("1")
case i > 1 && i < 3:fmt.Println("2")
default:fmt.Println("default")
}
----------------------------------
1
Type Switch 判断接口类型
type Test struct {name string
}func main() {var test1 = Test{"测试"}switch i := interface{}(test1).(type) {case nil:fmt.Printf(" x 为空")case int:fmt.Printf("x 是 int 型")case bool, string:fmt.Printf("x 是 bool 或 string 型")default:fmt.Printf("其他类型 %T", i)}
}
--------------------------------
其他类型 main.Test
Select
- select 是类似于 switch 的存在,但是他的分支需要绑定到通信操作上,select 中的 case 都是通信操作,要么接受,要么发送。
- 在一开始所有 channel 表达式都会被求值,如果其中任意一个通信可以进行,那么他就会执行,其他的会被忽略。
- 如果有多个case都可以运行,select 会随机公平地选出一个执行。其他不会执行。
- 如果没有分支可执行,但有 default 子句,则执行 default 语句。
- 如果没有分支可执行,且没有 default 字句,select 将阻塞,直到某个通信可以运行。
如下代码,如果我们不屏蔽 default 分支,正常情况下最终将会打印十次 none value 结果,因为我们在执行 select 的时候,我们创建的三个通道里面都还没有值,过100 ms 之后我们才会放值,select 将走 default 分支。
如果我们屏蔽掉 defalut 分支,打印结果如下,每一次的执行结果都可能不一样,哪一个通道中能先获取到值,就会执行对应分支的逻辑。
func goFunc(i int, ch *chan int) {time.Sleep(time.Millisecond * 100)*ch <- i
}func main() {ch0 := make(chan int, 3)ch1 := make(chan int, 3)ch2 := make(chan int, 3)for i := 0; i < 10; i++ {ch0 = make(chan int, 3)ch1 = make(chan int, 3)ch2 = make(chan int, 3)go goFunc(0, &ch0)go goFunc(1, &ch1)go goFunc(2, &ch2)select {case v := <-ch0:fmt.Printf("ch0 current value is %d\n", v)case v := <-ch1:fmt.Printf("ch1 current value is %d\n", v)case v := <-ch2:fmt.Printf("ch2 current value is %d\n", v)// default:// fmt.Printf("none value\n")}}//等待一段时间time.Sleep(time.Millisecond * 5000)
}
----------------------
ch1 current value is 1
ch1 current value is 1
ch0 current value is 0
ch1 current value is 1
ch2 current value is 2
ch0 current value is 0
ch2 current value is 2
ch0 current value is 0
ch1 current value is 1
ch2 current value is 2
同理,我们也可以通过校验通道是否可以写入值来控制分支。修改一下上面的代码,将其中一个分支修改为向通道中写值
执行到 select 时,所有通道中都没有值,但是 ch0 可以写入,所以会走第一个分支,打印结果全是 ch0 set value 9
func goFunc(i int, ch *chan int) {...}func main() {
...select {case ch0 <- 9:fmt.Printf("ch0 set value %d\n", <-ch0)
...}}
...
}
----------------------
ch0 set value 9
ch0 set value 9
ch0 set value 9
ch0 set value 9
ch0 set value 9
ch0 set value 9
ch0 set value 9
ch0 set value 9
ch0 set value 9
ch0 set value 9