Go基础
Go语言 流程控制
## 1. 条件语句if - 可省略条件表达式括号。 - 持初始化语句,可定义代码块局部变量。 - 代码块左 括号必须在条件表达式尾部。 - 不支持三元操作符(三目运算符) "a > b ? a : b"。 ```go if 布尔表达式 { /* 在布尔表达式为 true 时执行 */ } if 布尔表达式 { /* 在布尔表达式为 true 时执行 */ } else { /* 在布尔表达式为 false 时执行 */ } if 布尔表达式 1 { /* 在布尔表达式 1 为 true 时执行 */ if 布尔表达式 2 { /* 在布尔表达式 2 为 true 时执行 */ } } ``` ```go if n := "abc"; x > 0 { // 初始化语句未必就是定义变量, 如 println("init") 也是可以的。 println(n[2]) } else if x < 0 { // 注意 else if 和 else 左大括号位置。 println(n[1]) } else { println(n[0]) } ``` ## 2. 条件语句switch ```go switch var1 { case val1: ... case val2: ... default: ... } switch x.(type){ case type: statement(s) case type: statement(s) /* 你可以定义任意个数的case */ default: /* 可选 */ statement(s) } ``` ```go func main() { var x interface{} //写法一: switch i := x.(type) { // 带初始化语句 case nil: fmt.Printf(" x 的类型 :%T\r\n", i) case int: fmt.Printf("x 是 int 型") case float64: fmt.Printf("x 是 float64 型") case func(int) float64: fmt.Printf("x 是 func(int) 型") case bool, string: fmt.Printf("x 是 bool 或 string 型") default: fmt.Printf("未知型") } //写法二 var j = 0 switch j { case 0: case 1: fmt.Println("1") case 2: fmt.Println("2") default: fmt.Println("def") } //写法三 var k = 0 switch k { case 0: println("fallthrough") fallthrough /* Go的switch非常灵活,表达式不必是常量或整数,执行的过程从上至下,直到找到匹配项; 而如果switch没有表达式,它会匹配true。 Go里面switch默认相当于每个case最后带有break, 匹配成功后不会自动向下执行其他case,而是跳出整个switch, 但是可以使用fallthrough强制执行后面的case代码。 */ case 1: fmt.Println("1") case 2: fmt.Println("2") default: fmt.Println("def") } //写法三 var m = 0 switch m { case 0, 1: fmt.Println("1") case 2: fmt.Println("2") default: fmt.Println("def") } //写法四 var n = 0 switch { //省略条件表达式,可当 if...else if...else case n > 0 && n < 10: fmt.Println("i > 0 and i < 10") case n > 10 && n < 20: fmt.Println("i > 10 and i < 20") default: fmt.Println("def") } } ``` ## 3. 条件语句select - select是Go中的一个控制结构,类似于switch语句,用于处理异步IO操作。select会监听case语句中channel的读写操作,当case中channel读写操作为非阻塞状态(即能读写)时,将会触发相应的动作。 select中的case语句必须是一个channel操作 - select中的default子句总是可运行的。 - 如果有多个case都可以运行,select会随机公平地选出一个执行,其他不会执行。 - 如果没有可运行的case语句,且有default语句,那么就会执行default的动作。 - 如果没有可运行的case语句,且没有default语句,select将阻塞,直到某个case通信可以运行 - 与switch语句可以选择任何使用相等比较的条件相比,select由比较多的限制,其中最大的一条限制就是每个case语句里必须是一个IO操作 - 语法 ```go select { case communication clause : statement(s); case communication clause : statement(s); /* 你可以定义任意数量的 case */ default : /* 可选 */ statement(s); } select { //不停的在这里检测 case <-chanl : //检测有没有数据可以读 //如果chanl成功读取到数据,则进行该case处理语句 case chan2 <- 1 : //检测有没有可以写 //如果成功向chan2写入数据,则进行该case处理语句 //假如没有default,那么在以上两个条件都不成立的情况下,就会在此阻塞//一般default会不写在里面,select中的default子句总是可运行的,因为会很消耗CPU资源 default: //如果以上都没有符合条件,那么则进行default处理流程 } ``` - 以下描述了 select 语句的语法: ```go 每个case都必须是一个通信 所有channel表达式都会被求值 所有被发送的表达式都会被求值 如果任意某个通信可以进行,它就执行;其他被忽略。 如果有多个case都可以运行,Select会随机公平地选出一个执行。其他不会执行。 否则: 如果有default子句,则执行该语句。 如果没有default字句,select将阻塞,直到某个通信可以运行;Go不会重新对channel或值进行求值。 ``` - 基本使用 ```go package main import "fmt" func main() { var c1, c2, c3 chan int var i1, i2 int select { case i1 = <-c1: fmt.Printf("received ", i1, " from c1\n") case c2 <- i2: fmt.Printf("sent ", i2, " to c2\n") case i3, ok := (<-c3): // same as: i3, ok := <-c3 if ok { fmt.Printf("received ", i3, " from c3\n") } else { fmt.Printf("c3 is closed\n") } default: fmt.Printf("no communication\n") } } ``` - 超时判断 ```go //比如在下面的场景中,使用全局resChan来接受response,如果时间超过3S,resChan中还没有数据返回,则第二条case将执行 var resChan = make(chan int) // do request func test() { select { case data := <-resChan: doData(data) case <-time.After(time.Second * 3): fmt.Println("request time out") } } func doData(data int) { //... } ``` - 退出 ```go //主线程(协程)中如下: var shouldQuit=make(chan struct{}) fun main(){ { //loop } //...out of the loop select { case <-c.shouldQuit: cleanUp() return default: } //... } //再另外一个协程中,如果运行遇到非法操作或不可处理的错误,就向shouldQuit发送数据通知程序停止运行 close(shouldQuit) ``` - 判断channel是否阻塞 ```go //在某些情况下是存在不希望channel缓存满了的需求的,可以用如下方法判断 ch := make (chan int, 5) //... data:=0 select { case ch <- data: default: //做相应操作,比如丢弃data。视需求而定 } ``` ## 4. 循环语句for - 语法:支持三种循环方式 ```go for init; condition; post { } for condition { } for { } init: 一般为赋值表达式,给控制变量赋初值; condition: 关系表达式或逻辑表达式,循环控制条件; post: 一般为赋值表达式,给控制变量增量或减量。 for语句执行过程如下: ①先对表达式 init 赋初值; ②判别赋值表达式 init 是否满足给定 condition 条件,若其值为真,满足循环条件,则执行循环体内语句,然后执行 post,进入第二次循环,再判别 condition;否则判断 condition 的值为假,不满足条件,就终止for循环,执行循环体外语句。 ``` ```go func main() { var b int = 15 var a int numbers := [6]int{1, 2, 3, 5} /* for 循环 */ for a := 0; a < 10; a++ { fmt.Printf("a 的值为: %d\n", a) } for a < b { a++ fmt.Printf("a 的值为: %d\n", a) } for i,x:= range numbers { fmt.Printf("第 %d 位 x 的值 = %d\n", i,x) } } ``` - 循环嵌套 ```go for [condition | ( init; condition; increment ) | Range]{ for [condition | ( init; condition; increment ) | Range] { statement(s) } statement(s) } ``` ```go 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) } } } ``` - 无限循环 ```go func main() { for true { fmt.Printf("这是无限循环。\n"); } } ``` ## 循环语句range range类似迭代器操作,返回 (索引, 值) 或 (键, 值)。 for 循环的 range 格式可以对 slice、map、数组、字符串等进行迭代循环。格式如下: ```go for key, value := range oldMap { newMap[key] = value } ``` ```go func main() { s := "abc" // 忽略 2nd value,支持 string/array/slice/map。 for i := range s { println(s[i]) } //可忽略不想要的返回值,或 "_" 这个特殊变量。 // 忽略 index。 for _, c := range s { println(c) } // 忽略全部返回值,仅迭代。 for range s { } m := map[string]int{"a": 1, "b": 2} // 返回 (key, value)。 for k, v := range m { println(k, v) } } ``` ```go * 注意,range 会复制对象。 func main() { a := [3]int{0, 1, 2} for i, v := range a { // index、value 都是从复制品中取出。 if i == 0 { // 在修改前,我们先修改原数组。 a[1], a[2] = 999, 999 fmt.Println(a) // 确认修改有效,输出 [0, 999, 999]。 } a[i] = v + 100 // 使用复制品中取出的 value 修改原数组。 } fmt.Println(a) // 输出 [100, 101, 102]。 } ``` ```go //建议改用引用类型,其底层数据不会被复制。 func main() { 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) } } ``` - for 和 for range有什么区别? - 主要是使用场景不同 - for可以 - 遍历array和slice - 遍历key为整型递增的map - 遍历string - for range可以完成所有for可以做的事情,却能做到for不能做的,包括 - 遍历key为string类型的map并同时获取key和value - 遍历channel ## 循环控制Goto、Break、Continue 1. 三个语句都可以配合标签(label)使用 2. 标签名区分大小写,定以后若不使用会造成编译错误 3. continue、break配合标签(label)可用于多层循环跳出 4. goto是调整执行位置,与continue、break配合标签(label)的结果并不相同
顶部
收展
底部
[TOC]
目录
Go语言 简介
Go语言 项目结构
Go语言 程序结构
Go语言 流程控制
Go语言 变量常量
相关推荐
Go数据类型
Go函数
Go面向对象
Go并发编程
Go数据操作