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\n",a,b)
}
}
fmt.Printf("a 值为:%d\n",a)
fmt.Printf("b 值为:%d\n",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 [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 是素数\n",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\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("未知型")
}
}
-
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\n")
case c2 <- i2:
fmt.Printf("send ",i2," to c2\n")
case 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")
}
}
// 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\t", i, j, i*j)
}
fmt.Println("")
}
}
|
请发表评论