Go学习笔记 -- 流程控制 for、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
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mingvvv

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值