Go语言流程控制

本文详细介绍了Go语言中的条件语句(if、if...else、if...elseif...else、switch、case、default),以及select、for循环(包括for、forrange、死循环)、循环控制语句(break、continue、goto)的用法和示例。
摘要由CSDN通过智能技术生成

        条件语句需要开发者通过指定一个或多个条件,并通过测试条件是否为true来决定是否执行指定语句,并在条件为false的情况下执行另外的语句。

        Go语言提供了一下几种条件判断语句

一. 条件语句if

        1.1 if语句由一个布尔表达式后紧跟一个或多个语句组成

        语法:

if 布尔表达式{
    /*在布尔表达式为true时执行*/
}

        特点:

  • 可省略条件表达式括号
  • 持初始化语句,可定义代码块局部变量
  • 代码左括号必须在条件表达式尾部
  • Go语言不支持三目运算符 "a > b ? a : b"
package main

import "fmt"

func main() {
	n := 0
	//报错
	// if n > 0
	// {

	// }

	if str := "abc"; n > 0 { //初始化语句 不一定是定义变量,如println("init")也可以
		fmt.Println(str[2])
	} else if n < 0 { //注意else if 和else左大括号位置
		fmt.Println(str[1])
	} else {
		fmt.Println(str[0])
	}
}

        if在布尔表达式为true时,其后紧跟的语句块执行,如果为false则不执行。 

         1.2 if ... else语句

        if语句后可以使用可选的else语句,else语句中的表达式在布尔表达式为false时执行。

        语法:

if 布尔表达式 {
    /*在布尔表达式为true时执行*/
} else {
    /*在布尔表达式为false时执行*/
}

        实例:

        1.3 if ... else if ... else语句

        语法:

if 布尔表达式1 {
    /*布尔表达式1为true*/
} else if 布尔表达式2{
    /*布尔表达式1为false, 布尔表达式2为true*/
} else {
    /*布尔表达似1和布尔表达式2为false*/
}

         实例:

        1.4 if嵌套语句

        语法:

if 布尔表达式1 {
    /*在布尔表达式1为true时执行*/
    if 布尔表达式2 {
        /*在布尔表达式2为true时执行*/
    }
}

        实例:

二.条件语句switch 

         2.1 switch语句

        switch语句用于基于不同条件执行不同的动作,每个分支的case都是唯一的,从上往下逐一测试,直到匹配为止。

        Golang的switch分支表达式可以是任意类型,不限于常量。可省略break,默认自动终止。

        当所有的case都匹配不上,执行default里的代码。

        语法:

switch var1 {
    case val1:
        ...
    case val2:
        ...
    default:
        ...
}

         变量var1可以是任意类型,而val1和val2可以是同类型的任意值。类型不被局限于常量或者整数,但必须是同类型,或者最终结果为相同类型的表达式。

        case中可以同时测试多个可能符合条件的值,使用逗号分隔它们。例如:case val1, val2, val3。

        实例:

         2.2 Type Switch

        switch语句还可以被用于type-switch来判断某个interface变量中实际存储的变量类型。

        语法:

switch x.(type) {
    case type1:
        statement1(s)
    case type2:
        statement1(s)
    /*你可以定义任意个数的case*/
    default:
        statementn(s)
}

        实例:

  • Go语言switch非常灵活,表达式可以不是常量或整数,执行过程从上到下,直到找到匹配的。   
  • 如果switch没有表达式,它会匹配case后值为true的
  • Go里面switch默认每个case最后带有break,匹配成功不会自动向下执行其他case,而是跳出整个switch
  • 可以使用fallthrough强制执行后面的case代码,不管匹不匹配
package main

import "fmt"

func main() {
	var x interface{}
	//写法1
	switch i := x.(type) { //带初始化语句
	case nil:
		fmt.Printf("x 的类型: %T\r\n", i)
	case int:
		fmt.Printf("x 的类型是int\r\n")
	case float64:
		fmt.Printf("x 的类型是float64\r\n")
	case func(int) float64:
		fmt.Printf("x 的类型是func(int)\r\n")
	case bool, string:
		fmt.Printf("x 的类型是bool或string\r\n")
	default:
		fmt.Printf("类型未知\r\n")
	}

	//写法2 没有输出
	var j = 0
	switch j {
	case 0:
	case 1:
		fmt.Println("1")
	case 2:
		fmt.Println("2")
	default:
		fmt.Println("def")
	}

	//写法3
	var k = 0
	switch k {
	case 0:
		fmt.Println("fallthrough")
		fallthrough
	case 1:
		fmt.Println("1")
	case 2:
		fmt.Println("2")
	default:
		fmt.Println("def")
	}

	//写法4
	var m = 0
	switch m {
	case 0, 1:
		fmt.Println("1")
	case 2:
		fmt.Println("2")
	default:
		fmt.Println("def")
	}

	//写法5
	var n = 0
	switch {
	case n > 0 && n < 10:
		fmt.Println("n > 0 && n < 10")
	case n > 10 && n < 20:
		fmt.Println("n > 0 && n < 10")
	default:
		fmt.Println("def")
	}
}

三.条件语句select

        1.1 介绍

        select是Go的一个控制结构,类似于用于通信的switch语句,每一个case必须是一个通信操作,要么是发送,要么是接收。select随机执行一个可运行的case。如果没有case可以运行,它将堵塞,直到有case可以运行。一个默认子句应该总是可运行的。 

        语法:

  • 每一个case必须是一个通信
  • 所有channel表达式都会被求值
  • 所有被发送的表达式都会被求值
  • 如果任意某个通信可以进行,它就执行,其他被忽略
  • 如果有多个case可以运行,select会随机公平的选出一个执行。其它不会执行
  • 如果有default,没有case可以执行,执行default语句
  • 如果没有default语句,没有case可以执行,select将阻塞,直到某个通信可以运行,Go不会重新对channel或值进行求值。
select {
case communication clause:
    statement(s)
case communication clause:
    statement(s)
/*你可以定义任意数量的case*/

default:/*可选*/
    statement(s)

}

        实例:

  • 执行过程 

         select可以监听channel的数据流动。select的用法于switch语法非常类似,由select开始的一个新的选择块,每一个选择条件由case语句类描述。

        与switch语句可以选择任何使用相等比较的条件相比,select有比较多的限制条件,其中最大的一条限制条件就是每一个case语句必须是一个I/O操作。

select {
case <-chan1://检测有没有数据可读
    //如果chan1成功读取到数据,则执行该case处理语句
case chan2<-1://检测有没有可以写
    //如果成功向chan2写入数据,则进行该case处理语句

//假如没有default,那么在以上两个条件都不成立的情况下,就会在此阻塞
//一般default不会写在里面,select中的default子句总是可以运行的,因为会很消耗CPU资源
default:
    //如果以上都没有符合条件,那么则进行default处理流程
}

在select语句中,Go会按顺序从头到尾评估每一个发送和接收语句。

如果其中的任意一个语句可以继续执行(即没有被阻塞),那么就从那些可以执行的语句中任意选择一条来使用。

如果没有一条语句可以执行(即所有的通道都被阻塞),那么有两种情况:

  • 如果给出default语句,那么就会执行default的流程,然后程序会继续往下执行
  • 如果没有default语句,那么select语句将被阻塞,直到至少有一个case可以进行下去

        1.2 Golang 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,如果时间超过3秒,resChan中还没有数据返回,则第二个case将执行。

package main

import (
	"fmt"
	"time"
)

var resChan = make(chan int)

func test() {
	select {
	case data := <-resChan:
		doData(data)
	case <-time.After(time.Second * 3):
		fmt.Println("request time out")
	}
}

func doData(data int) {

}

func main() {
	test()
}
  • 退出

        在另外协程中,如果运行遇到非法操作或者不可处理的错误,就像shouldQuit发送数据通知程序停止运行。

package main

var shouldQuit = make(chan struct{})

func clearUp() {

}
//主协程
func main() {
	{
		//loop
	}

	//out of loop
	select {
	case <-shouldQuit:
		clearUp()
		return
	default:
	}
	//...
}

close(shouldQuit)
  • 判断channel是否阻塞

        在某些情况下是存在不希望channel缓存满的需求

package main

var ch = make(chan int, 5)

func main() {
	data := 0
	select {
	case ch <- data:
	default:
		//做相应操作,比如丢弃data s视需求而定
	}
}

四. 循环语句for

        1.1 三种循环方式

        Golang支持三种循环方式,包括类似while的语法。

        for循环是一个循环控制结构,可以执行指定次数的循环。

        语法:

        Go语言的for循环有3种形式,只有其中的一种使用分号。

  • for init; condition;post{}
    • init:一般为赋值表达式,给控制变量赋初值
    • condition:关系表达式或逻辑表达式,循环控制条件
    • post:一般为赋值表达式,给控制表里增量或减量
  • for condition{}
  • for {}

执行过程:

  1. 先对表达式init赋初值
  2. 判断赋值表达式init是否满足给定condition条件,若其值为真,满足循环条件,则执行循环体内语句,然后执行post,进入第二次循环,再判别condition,当condition的值为假,不满足条件,就终止循环。

        实例: 

package main

import "fmt"

func main() {
	s := "abc"

	for i := 0; i < len(s); i++ {
		fmt.Println(s[i])
	}

	n := len(s)
	for n > 0 {
		fmt.Println(s[n-1])
		n--
	}

	//死循环
	for {
		fmt.Println(s)
	}
}

         我们可以在初始化语句中计算出全部结果:

        for循环上初始化的变量,只在当前for 循环内有效。

package main

import "fmt"

func main() {

	//for初始化定义的值不会受已定义的同名变量影响
	//并且不能再for循环外使用
	for a := 0; a < 10; a++ {
		fmt.Printf("a的值为%d\n", a)
	}
	//报错
	//fmt.Println(a)

	var a, b = 0, 15
	for a < b {
		fmt.Printf("a的值为%d\n", a)
		a++
	}
}

        1.2 循环嵌套

        实例:

        使用循环嵌套来输出2到100间的素数:

package main

import "fmt"

func main() {
	for i := 2; i <= 100; i++ {
		j := 2
		for ; j <= (i / j); j++ { //只需要检测一半的数
			if i%j == 0 {
				break //不是素数
			}
		}
		if j > (i / j) {
			fmt.Printf("素数%d\n", i) //遍历完 说明是素数
		}
	}
}

         1.3 死循环

        如果循环中的条件永远不会为false,则会进行无限循环。

package main

import "fmt"

func main() {
	for {
		fmt.Println("这是死循环")
	}

	for true {
		fmt.Println("这是死循环")
	}
}

五. 循环语句range

        Golang range类似迭代器操作,返回(索引,值)或(键,值)。

        for循环的range格式对slice,map,数组,字符串等进行迭代循环。格式如下:

for key, value := range oldMap {
    newMap[key] = value
} 

         可以忽略不想要的返回值,或"_"这个特殊变量。

package main

func main() {
	s := "abc"

	//忽略value,支持string/array/slice/map
	for i := range s {
		println(s[i])
	}

	//忽略index
	for _, val := range s {
		println(val)
	}

	//忽略全部返回值,仅迭代
	for range s {

	}

	m := map[string]int{"a": 1, "b": 2}
	for k, v := range m {
		println(k, v)
	}
}
  •  注意

        在使用range进行遍历的时候,如果遍历的是值类型对象。首先会复制一份遍历的数据,然后键/索引和值是从复制的数据中取出来的。所以当修改原数组或者range后的值,互相不会产生影响。

        如果是引用类型(slice,map,chan等),其底层数据不会被复制,复制的只是指针,但是指针指向的底层数据一样。

        所以修改原数据,即修改了底层数据,会影响range后的值。但是修改range后的值,不会影响原数据,因为range后的值是一份拷贝。

        对引用类型range的数据范围进行修改不会影响range。

        5.1 for和for range的区别

主要是使用场景不同:

        for可以遍历数组,切片,key为整型递增的map和string。

        for range可以完成所有for可以做的事,也能做for不能做的事,包括遍历key为string类型的map,并同时获取key和value。 还可以遍历channel。

六.循环控制语句

        循环控制语句可以控制循环体语句的执行过程。循环控制语句包括break,continue,goto。

        Go语言的标签可以用来标记代码特定的位置。 

  • 三个语句都可以配合标签使用
  • 标签名区分大小写,定义后若不使用会造成编译错误
  • continue和break配合标签可用于多层循环的退出
  • goto是调整执行位置,与break,continue配合标签结果不同
  • break

        在循环中的作用是跳出循环。但是只能跳出当前循环,无法跳出多层循环。

         想跳出到指定位置,可以结合标签实现。标志位置表示要跳出的循环。

  • continue 

        在循环中的意思是当次循环后续语句不再执行,但是不会跳出循环。

        结合标志,表示要结束的单次循环的循环。标志指定的for循环,continue后的语句都不会执行。

  • goto 

        goto需要结合标志来使用,表示直接跳到对应的代码逻辑。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值