1.go语言流程控制
1.if条件语句
-
流程控制是每种编程语言控制逻辑走向和执行次序的重要部分,流程控制可以说是一门语言的“经脉”。
Go语言中最常用的流程控制有
if
和for
,而switch
和goto
主要是为了简化代码、降低重复代码而生的结构,属于扩展类的流程控制。 -
if 条件判断基本写法:
if 表达式1{ 分支1 }else if 表达式2{ 分支2 }else{ 分支3 }
-
if条件语句:在布尔表达式为true时,其后紧跟的语句块执行,如果为false则不执行。
package main import "fmt" func main() { //基本写法 var score = 65 if score >= 90 { fmt.Println("A") } else if score > 75 { fmt.Println("B") // 注意 else if 和 else 左大括号位置。 } else { fmt.Println("C") } }
-
if判断特殊写法
- if条件判断还有一种特殊的写法,可以在 if 表达式之前添加一个执行语句,再根据变量值进行判断,举个例子:
func main() { //2.if判断特殊写法 //此时score在if代码块中才生效 if score := 65; score >= 90 { fmt.Println("A") } else if score > 75 { fmt.Println("B") } else { fmt.Println("C") } }
- 不支持三元操作(三目运算符) "a > b ? a:b"
-
if语句嵌套
package main import "fmt" func main(){ var a int = 10 var b int = 20 if a == 10 { if b == 20 { fmt.Printf("a=%d,b=%d ",a,b) } } fmt.Printf("a 值为:%d ",a) fmt.Printf("b 值为:%d ",b) }
2.for循环语句
-
Go 语言中的所有循环类型均可以使用
for
关键字来完成。 -
Go语言中for循环有3种形式,只有其中一种使用分号:
1.for init;condition;post {} 2.for condition {} 3.for {} init : 一般为赋值表达式,给控制变量赋值; condition:关系表达式或逻辑表达式,循环控制条件; post:一般为赋值表达式,给控制变量增量或减量; for语句执行过程如下: 1.先对表达式 init 赋初始值; 2.判断赋值表达式init是否满足给定condition条件,若其值为真,满足循环条件,则执行循环体内语句,然后执行 post,进入第二次循环,再判别 condition;否则判断 condition 的值为假,不满足条件,就终止for循环,执行循环体外语句。
-
for循环基本格式如下:
for 初始语句;条件表达式;结束语句{ 循环体语句 }
-
条件表达式返回
true
时循环体不停地进行循环,直到条件表达式返回false
时自动退出循环。 -
for 循环
package main
import "fmt"
//for循环
func main() {
for i := 0; i < 10; i++ {
fmt.Println(i)
}
}
-
省略初始语句,但是必须保留初始语句的分号
func main() { var i = 0 for ; i < 10; i++ { fmt.Println(i) } }
-
省略初始语句和结束语句
func main() { var i = 10 for i > 0 { fmt.Println(i) i-- } }
-
无限循环
for { fmt.Println("hello shahe") }
-
break 跳出for循环
for i := 0; i < 5; i++ { fmt.Println(i) if i == 3 { break } }
-
continue继续下一次循环
for i := 0; i < 5; i++ { if i == 3 { continue //跳过本次for循环,继续下一次循环 } fmt.Println(i) } //0 //1 //2 //4
-
循环的嵌套
- for循环中嵌套一个或多个for循环
for [condition | ( init; condition; increment ) | Range] { for [condition | ( init; condition; increment ) | Range] { statement(s) } statement(s) }
-
循环嵌套输出2-100素数
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 是素数 ",i) } } }
3.switch case循环
-
switch语句用于基于不同条件执行不同动作,每个case分支都是唯一,从上直下逐一测试,直到匹配为止。 Golang switch 分支表达式可以是任意类型,不限于常量。可省略 break,默认自动终止。
switch var1 { case val1: ... case val2: ... default: ... }
变量 var1 可以是任何类型,而 val1 和 val2 则可以是同类型的任意值。类型不被局限于常量或整数,但必须是相同的类型;或者最终结果为相同类型的表达式。 您可以同时测试多个可能符合条件的值,使用逗号分割它们,例如:case val1, val2, val3。
-
使用
switch
语句可方便地对大量的值进行条件判断。package main import "fmt" //switch func main() { finger := 3 switch finger { case 1: fmt.Println("大拇指") case 2: fmt.Println("食指") case 3: fmt.Println("中指") case 4: fmt.Println("无名指") case 5: fmt.Println("小拇指") default: fmt.Println("无效输入") } } //go语规定每个switch只能有一个default分支。一个分支可以有多个值,多个case值中间使用英文逗号分隔。
-
case一次判断多个值
num := 5
switch num {
case 1, 3, 5, 7, 9:
fmt.Println("奇数")
case 2, 4, 6, 8:
fmt.Println("偶数")
}
- case 中使用表达式
- 分支还可以使用表达式,这时候switch语句后面不需要再跟判断变量
age := 30
switch {
case age > 18:
fmt.Println("成年人")
case age < 18:
fmt.Println("未成年人")
default:
fmt.Println("不是人")
}
-
fallthrough
语法可以执行满足条件的case的下一个case.是为了兼容C语言中case设计的。 -
Type Switch
- switch 语句还可以被用于 type-switch 来判断某个 interface 变量中实际存储的变量类型。
- 语法:
switch x.(type) { case type: statement(s) case type: statement(s) default: statement(s) }
-
demo1
func main(){ var x interface{} // 带初始化语句 switch i := x.(type) { case nil: fmt.Printf("x的类型:%T ",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("未知型") } }
-
demo2
func main() { var j = 0 switch j { case 0: case 1: fmt.Println("1") case 2: fmt.Println("2") default: fmt.Println("def") } }
-
demo3
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") } // fallthrough // 1
4.条件语句select
-
select 语句类似于 switch 语句,但是select会随机执行一个可运行的case。如果没有case可运行,它将阻塞,直到有case可运行。
-
select 是Go中的一个控制结构,类似于用于通信的switch语句。每个case必须是一个通信操作,要么是发送要么是接收。 select 随机执行一个可运行的case。如果没有case可运行,它将阻塞,直到有case可运行。一个默认的子句应该总是可运行的。
-
Go 编程语言中 select 语句的语法如下:
select { case communication clause : statement(s); case communication clause : statement(s); /* 你可以定义任意数量的 case */ default : /* 可选 */ statement(s); }
-
select语句的语法:
每个case都必须是一个通信 所有channel表达式都会被求值 所有被发送的表达式都会被求值 如果任意某个通信可以进行,它就执行;其他被忽略。 如果有多个case都可以运行,Select会随机公平地选出一个执行。其他不会执行。 否则: 如果有default子句,则执行该语句。 如果没有default字句,select将阻塞,直到某个通信可以运行;Go不会重新对channel或值进行求值。
-
实例:
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 ") case c2 <- i2: fmt.Printf("send ",i2," to c2 ") case i3,ok := (<-c3): if ok { fmt.Printf("received ", i3, " from c3 ") } else { fmt.Printf("c3 is closed ") } default: fmt.Printf("no communication ") } } // no communication
select可以监听channel的数据流动
select的用法与switch语法非常类似,由select开始的一个新的选择块,每个选择条件由case语句来描述
与switch语句可以选择任何使用相等比较的条件相比,select由比较多的限制,其中最大的一条限制就是每个case语句里必须是一个IO操作
select { //不停的在这里检测 case <-chanl : //检测有没有数据可以读 //如果chanl成功读取到数据,则进行该case处理语句 case chan2 <- 1 : //检测有没有可以写 //如果成功向chan2写入数据,则进行该case处理语句 //假如没有default,那么在以上两个条件都不成立的情况下,就会在此阻塞//一般default会不写在里面,select中的default子句总是可运行的,因为会很消耗CPU资源 default: //如果以上都没有符合条件,那么则进行default处理流程 }
在一个select语句中,Go会按顺序从头到尾评估每一个发送和接收的语句。
如果其中的任意一个语句可以继续执行(即没有被阻塞),那么就从那些可以执行的语句中任意选择一条来使用。 如果没有任意一条语句可以执行(即所有的通道都被阻塞),那么有两种可能的情况: ①如果给出了default语句,那么就会执行default的流程,同时程序的执行会从select语句后的语句中恢复。 ②如果没有default语句,那么select语句将被阻塞,直到至少有一个case可以进行下去。
-
Go语言中select的使用及典型用法
-
select是Go中的一个控制结构,类似于switch语句,用于处理异步IO操作。select会监听case语句中channel的读写操作,当case中channel读写操作为非阻塞状态(即能读写)时,将会触发相应的动作。 select中的case语句必须是一个channel操作
select中的default子句总是可运行的。
如果有多个case都可以运行,select会随机公平地选出一个执行,其他不会执行。
如果没有可运行的case语句,且有default语句,那么就会执行default的动作。
如果没有可运行的case语句,且没有default语句,select将阻塞,直到某个case通信可以运行。
-
超时判定
//比如在下面的场景中,使用全局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) { //... }
-
退出
//主线程(协程)中如下: var shouldQuit=make(chan struct{}) fun main(){ { //loop } //...out of the loop select { case <-c.shouldQuit: cleanUp() return default: } //... } //再另外一个协程中,如果运行遇到非法操作或不可处理的错误,就向shouldQuit发送数据通知程序停止运行 close(shouldQuit)
-
判断channel是否阻塞
//在某些情况下是存在不希望channel缓存满了的需求的,可以用如下方法判断 ch := make (chan int, 5) //... data:=0 select { case ch <- data: default: //做相应操作,比如丢弃data。视需求而定 }
-
5.循环语句range
-
Golang range 类似迭代器操作,返回索引,值或键,值
-
for循环的range格式可以对slice,map,数组,字符串等进行迭代循环,格式如下:
for key,value := range oldMap{ newMap[key] = value }
-
课忽略不想要的返回值,或 "_" 这个特殊变量
package main func main() { s := "abcde" // 忽略value for i:=range s{ println(s[i]) } // 忽略index for _,v := range s{ println(v) } } package main func main() { m := map[string]int{"a":1,"b":2} for k,v := range m{ println(k,v) } }
-
range会复制对象
import "fmt" 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]。 } // [0 999 999] // [100 101 102]
-
建议改用引用类型,其底层数据不会被复制。slice是引用类型
package main 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) } } // 0 1 // 1 2 // 2 100 // 3 4
另外两种引用类型 map、channel 是指针包装,而不像 slice 是 struct。
-
for 和 for range有什么区别?
主要是使用场景不同 for可以 遍历array和slice 遍历key为整型递增的map 遍历string for range可以完成所有for可以做的事情,却能做到for不能做的,包括 遍历key为string类型的map并同时获取key和value 遍历channel
6.循环控制Goto,Break,Continue
1.三个语句都可以配合标签(label)使用
2.标签名区分大小写,不适用会造成编译错误
3.continue、break配合标签(label)可用于多层循环跳出
4.goto是调整执行位置,与continue、break配合标签(label)的结果并不相同
-
小测试:99乘法表
package main import "fmt" func main() { for i := 1; i < 10; i++ { for j := 1; j <= i; j++ { fmt.Printf("%v*%v=%v ", i, j, i*j) } fmt.Println("") } }