if ... else if ... else 语句
golang的if 条件中不需要加括号,并且if支持写声明语句
import "fmt"
func testIf1(c int) {
a := 1
// if 可以写声明语句
if b := 4; a < c && c > b {
fmt.Println("c")
} else if a = 3; a == c {
fmt.Println("a")
} else {
fmt.Println("b")
}
}
func main() {
testIf1(123)
testIf1(3)
testIf1(1)
}
switch语句
go的switch语句不用加break,会自动退出
func testSwitch1(score int) {
switch score {
case 1:
fmt.Println("E")
case 2:
fmt.Println("D")
case 3:
fmt.Println("C")
case 4:
fmt.Println("B")
case 5:
fmt.Println("A")
}
}
也可以显式break退出:
func testSwitch2(score int) {
switch score {
case 1:
fmt.Println("E")
case 2:
fmt.Println("is 2")
break
fmt.Println("D")
case 3:
fmt.Println("C")
case 4:
fmt.Println("B")
case 5:
fmt.Println("A")
}
}
可以使用fallthrough到下一个case:
func testSwitch3(score int) {
switch score {
case 1:
fmt.Println("E")
// case 可以是多个值
case 2,6:
fmt.Println("D")
//if score == 6 {
// fallthrough 不能写在新的变量环境里,只能至于case下面,这个if语句语法报错
// fallthrough
//}
// fallthrough 跳到下一个case
fallthrough
case 3:
fmt.Println("C")
case 4:
fmt.Println("B")
default:
// default 不能执行fallthrough 语法报错
//fallthrough
fmt.Println("default")
case 5:
fmt.Println("A")
// 最后一个case 不能执行fallthrough 语法报错
//fallthrough
}
}
可以省略变量,直接写条件语句
func testSwitch4(score int) {
switch {
case score > 3:
fmt.Println("more than C")
case score < 2:
fmt.Println("is e")
}
}
switch 支持写声明语句
func testSwitch5(t interface{}) {
switch typ := t.(type) {
case nil:
fmt.Println("is nil")
case int:
fmt.Printf("is int %d\n", typ)
case []int:
fmt.Printf("is []int %v\n", typ)
case func(int) int:
fmt.Println("is func(int) int")
default:
fmt.Println("is other thing")
}
}
for 循环
for init; condition; post { }
for condition { }
for { }
init: 一般为赋值表达式,给控制变量赋初值;
condition: 关系表达式或逻辑表达式,循环控制条件;
post: 一般为赋值表达式,给控制变量增量或减量。
for语句执行过程如下:
①先对表达式 init 赋初值;
②判别赋值表达式 init 是否满足给定 condition 条件,若其值为真,满足循环条件,则执行循环体内语句,然后执行 post,进入第二次循环,再判别 condition;否则判断 condition 的值为假,不满足条件,就终止for循环,执行循环体外语句。
func testFor1() {
a := [...]int{1,2,3,4,5}
// c中常见的循环遍历写法
for i,l := 0,len(a); i< l; i++ {
fmt.Printf("%d-%d\n", i, a[i])
}
// for range
// for 循环的 range 格式可以对 slice、map、数组、字符串等进行迭代循环。
for i,v := range a {
fmt.Printf("%d-%d\n", i, v)
}
// 遍历map
m := map[string]string {
"a":"aa",
"b":"bb",
}
for k,v := range m {
fmt.Printf("%s-%s\n", k, v)
}
// while
b := 1
for b < 10 {
fmt.Println(b)
b ++
}
// while(true)
c := 1
for {
fmt.Println(c)
c ++
if c == 10 {
break
}
}
}
需要注意for和range使用,range在循环时会自动进行值的拷贝,所以直接修改其中的值,是改不了的:
func testFor2() {
s := []int{1, 2, 3, 4, 5}
for i, v := range s { // 复制 struct slice { pointer, len, cap }。
if i == 0 {
s = s[:3] // 对 slice 的修改,不会影响 range。
s[2] = 100 // 对底层数据的修改。
}
println(i, v)
}
}
func testArr3() {
type A struct {
a string
}
arr := [...]A{
{a: "1"},
{a: "2"},
}
fmt.Println(arr)
for i, v := range arr {
// range 是值传递,因此无法直接修改对象的值,对比地址的结果是false
fmt.Println(&v == &arr[i])
v.a = "3"
}
fmt.Println(arr)
// 通过索引修改没有问题
for i, _ := range arr {
arr[i].a = "3"
}
fmt.Println(arr)
// 指针数组,遍历的是指针,如此修改值生效
arr2 := [...]*A{
{a: "1"},
{a: "2"},
}
fmt.Println(*arr2[0], *arr2[1])
for i, v := range arr2 {
fmt.Println(v == arr2[i])
v.a = "3"
}
fmt.Println(*arr2[0], *arr2[1])
}
testArr3的结果:
[{1} {2}]
false
false
[{1} {2}]
[{3} {3}]
{1} {2}
true
true
{3} {3}
查看下例,m中A的值会被改为666吗?答案是不会,因为A并不是指针引用,仍然会在遍历时进行值拷贝。
func testFor3() {
type A struct {
a string
}
m := map[string]A {
"a": A{"123"},
"b": A{"234"},
}
for _,v := range m {
v.a = "666"
}
fmt.Println(m)
}
for 循环还可以被用于遍历管道:
func testFor4() {
ch := make(chan int)
go func() {
// 需要关闭管道
defer close(ch)
for i := 0; i < 10 ; i ++{
ch <- i
time.Sleep(time.Millisecond * 500)
}
}()
go func() {
// for 遍历管道
for {
if data, ok := <-ch; true {
if ok {
fmt.Println("for", data)
} else {
break
}
}
}
/*
下例是错误写法,因为data, ok := <-ch只在循环初始化第一次执行一次
for data, ok := <-ch; true; {
if ok {
fmt.Println(data)
} else {
break
}
}
}
*/
}()
// 或者使用for range
go func() {
for v := range ch {
fmt.Println("range", v)
}
}()
time.Sleep(10 * time.Second)
}
上例的输出结果是随机的,因为有两个线程同时在消费管道。
for 还可以用于遍历字符串:
// byte
for i := 0; i < len(s); i++ {
fmt.Printf("%v(%c) ", s[i], s[i])
}
fmt.Println()
//自动处理unicode为rune
for _, r := range s {
fmt.Printf("%v(%c) ", r, r)
}
fmt.Println()
循环控制Goto、Break、Continue
break、continue用于跳出循环。goto则是原汁原味的c风格。三个语句都支持配合label标签使用。
下例如果去掉 end = true将会是个死循环。
func testGoto() {
i := 0
end := false
a:
fmt.Println(i)
if i < 10 {
i++
goto c
}
fmt.Println("i is 10")
end = true
c:
fmt.Println("run c")
if !end {
goto a
}
}
select语句
- 每个case都必须是一个通信
- 所有channel表达式都会被求值
- 所有被发送的表达式都会被求值
- 如果任意某个通信可以进行,它就执行;其他被忽略。
- 如果有多个case都可以运行,Select会随机公平地选出一个执行。其他不会执行。
否则: - 如果有default子句,则执行该语句。
- 如果没有default字句,select将阻塞,直到某个通信可以运行;Go不会重新对channel或值进行求值。
- select 语句并不是if else的替代品,其中随机执行可执行的case通过if else 是无法实现的
func testSelect1() {
var ch1 = make(chan int)
var ch2 = make(chan string)
go func() {
ch1 <- 233
}()
go func() {
ch2 <- "aaa"
}()
select {
case a := <-ch1:
fmt.Println("ch1", a)
case b := <-ch2:
fmt.Println("ch2", b)
}
}
select 语句会从多个可执行的case随机取一个执行,未执行的case将不会实际读取通道的数据
func testSelect6() {
ch1 := make(chan int, 2)
ch2 := make(chan int, 2)
fmt.Println(len(ch1))
fmt.Println(len(ch2))
go func() {
ch1 <- 1
ch2 <- 1
ch1 <- 2
ch2 <- 2
}()
time.Sleep(1 * time.Second)
fmt.Println(len(ch1))
fmt.Println(len(ch2))
// 有两个通道可以读取数据,但是只有随机执行到的case的通道才会真的读取
// 因此执行完select,一个通道的len为2一个为1
select {
case <- ch1:
fmt.Println("ch1")
case <- ch2:
fmt.Println("ch2")
}
fmt.Println(len(ch1))
fmt.Println(len(ch2))
}
select 相比switch 的一个特性在于,它默认是会阻塞的(没有default的情况),配合for + select 可以实现一个消费者,消费来自多个生产者的内容,例如:
func testSelect2() {
var ch1 = make(chan int)
var ch2 = make(chan string)
go func() {
for i := 0; i <= 10; i++ {
ch1 <- i
time.Sleep(100 * time.Millisecond)
}
}()
go func() {
ch2 <- "aaa"
time.Sleep(200 * time.Millisecond)
}()
a:
for {
select {
case a := <-ch1:
fmt.Println("ch1", a)
if a == 10 {
fmt.Println("end")
break a
}
case b := <-ch2:
fmt.Println("ch2", b)
}
}
}
还可以用于超时控制:
func testSelect3(seconds int) {
ch := make(chan int)
go func() {
time.Sleep(time.Duration(seconds) * time.Second)
ch <- 123
}()
select {
case v := <-ch:
fmt.Println("success", v)
// 当时间超出3s时自动退出
case <- time.After(3 * time.Second):
fmt.Println("timeout")
}
}
优雅退出:
func testSelect4() {
quitChan := make(chan struct{})
// 启动一个协程,过5秒钟告诉主线程可以退出了
go func() {
time.Sleep(5 * time.Second)
quitChan <- struct{}{}
}()
// 主线程
for {
fmt.Println("I am still alive.")
time.Sleep(1 * time.Second)
select {
case <-quitChan:
fmt.Println("clean something")
fmt.Println("quit")
return
default:
// 继续干活
}
}
}
判断通道是否已满
func testSelect5() {
ch := make(chan int, 5)
go func() {
for i := 0; i < 10; i++ {
ch <- i
}
}()
time.Sleep(1 * time.Second)
select {
// 不仅可以出还可以进,如果满了就会阻塞
case ch <- 0:
fmt.Println("队列未满")
// ...
default:
// 此时已经满了
fmt.Println(len(ch))
// 可以剔除一部分数据,此处剔除一半数据
// 此外可以做某些操作,比如启动更多的线程or服务来消费数据
for i, l := 0, len(ch); i < l/2; i++ {
a := <-ch
fmt.Println("del", a)
}
}
}
版权声明
本文章由作者“衡于墨”创作,转载请注明出处,未经允许禁止用于商业用途
评论区#
还没有评论哦,期待您的评论!
引用发言