Go开发 之 流程控制(if/else、for/range、switch、goto、break、continue)
  RmFdTfeluVsz 2023年11月02日 111 0


文章目录

  • ​​0、唠唠叨叨​​
  • ​​1、分支结构(if / else)​​
  • ​​1.1、标准写法​​
  • ​​1.2、特殊写法​​
  • ​​2、循环结构(for)​​
  • ​​2.1、标准循环​​
  • ​​2.2、无限循环​​
  • ​​2.3、for 中的初始语句(开始循环时执行的语句)​​
  • ​​2.4、for 中的结束语句(循环结束时执行的语句)​​
  • ​​2.5、for 中的条件表达式(控制是否循环的开关)​​
  • ​​2.5.1、结束循环时带可执行语句的无限循环​​
  • ​​2.5.2、无限循环​​
  • ​​2.5.3、只有一个循环条件的循环​​
  • ​​3、键值循环结构(for range)​​
  • ​​3.1、标准循环​​
  • ​​3.2、遍历数组、切片(获得索引和值)​​
  • ​​3.3、遍历字符串(获得字符)​​
  • ​​3.4、遍历 map(获得 map 的键和值)​​
  • ​​3.5、遍历通道 channel(接收通道数据)​​
  • ​​3.6、在遍历中选择希望获得的变量​​
  • ​​4、分支结构(switch / case)​​
  • ​​4.1、基本写法​​
  • ​​4.2、一分支多值​​
  • ​​4.3、分支表达式​​
  • ​​4.4、跨越 case 的 fallthrough(兼容C语言的 case 设计)​​
  • ​​5、跳转结构(break / continue / goto)​​
  • ​​5.1、跳出制定循环 break​​
  • ​​5.2、继续下一次循环 continue​​
  • ​​5.3、跳转到指定的标签 goto​​
  • ​​5.3.1、使用 goto 退出多层循环​​
  • ​​5.3.2、使用 goto 集中处理错误​​
  • ​​6、示例​​
  • ​​6.1、九九乘法表​​
  • ​​6.2、冒泡排序​​
  • ​​6.3、二分查找​​
  • ​​6.4、简易聊天机器人​​

0、唠唠叨叨

流程控制可以说是一门语言的经脉了。Go 语言的常用流程控制有 if 和 for,而 switch 和 goto 主要是为了简化代码、降低重复代码而生的结构,属于扩展类的流程控制。

本章主要介绍 Go 语言中的基本流程控制语句,包括分支语句(if 和 switch)、循环(for)和跳转(goto)语句。另外,还有循环控制语句(break 和 continue),break 的功能是中断循环或者跳出 switch 判断,continue 的功能是继续 for 的下一个循环。

1、分支结构(if / else)

1.1、标准写法

标准格式:

if condition1 {
// do something
} else if condition2 {
// do something else
}else {
// catch-all or default
}

​无效格式:​

if x{
}
else { // 无效的
}

1.2、特殊写法

将返回值与判断放在一行进行处理,而且返回值的作用范围被限制在 if、else 语句组合中。

if 还有一种特殊的写法,可以在 if 表达式之前添加一个执行语句,再根据变量值进行判断,例如:

if err := Connect(); err != nil {
fmt.Println(err)
return
}

Connect 是一个带有返回值的函数,err:=Connect() 是一个语句,执行 Connect 后,将错误保存到 err 变量中。err != nil 才是 if 的判断表达式,当 err 不为空时,打印错误并返回。

在编程中,变量的作用范围越小,所造成的问题可能性越小,每一个变量代表一个状态,有状态的地方,状态就会被修改,函数的局部变量只会影响一个函数的执行,但全局变量可能会影响所有代码的执行状态,因此限制变量的作用范围对代码的稳定性有很大的帮助。

2、循环结构(for)

Go语言中的循环语句只支持 for 关键字,而​​不支持​​while 和 do-while 结构。

2.1、标准循环

sum := 0
for i := 0; i < 10; i++ {
sum += i
}

2.2、无限循环

sum := 0
for {
sum++
if sum > 100 {
break
}
}

2.3、for 中的初始语句(开始循环时执行的语句)

初始语句是在第一次循环前执行的语句,一般使用初始语句执行变量初始化,如果变量在此处被声明,其作用域将被局限在这个 for 的范围内。初始语句可以被忽略,但是初始语句之后的分号必须要写,例如:

step := 2
for ; step > 0; step-- {
fmt.Println(step)
}

这段代码将 step 放在 for 的前面进行初始化,for 中没有初始语句,此时 step 的作用域就比在初始语句中声明 step 要大。

2.4、for 中的结束语句(循环结束时执行的语句)

在结束每次循环前执行的语句,如果循环被 break、goto、return、panic 等语句强制退出,结束语句不会被执行。

2.5、for 中的条件表达式(控制是否循环的开关)

2.5.1、结束循环时带可执行语句的无限循环

下面代码忽略条件表达式,但是保留结束语句,例如:

var i int
for ; ; i++ {
if i > 10 {
break
}
}

2.5.2、无限循环

上面的代码还可以改写为更美观的写法。

var i int
for {
if i > 10 {
break
}
i++
}

无限循环在收发处理中较为常见,但需要无限循环有可控的退出方式来结束循环。

2.5.3、只有一个循环条件的循环

满足条件表达式时持续循环,否则结束循环。

var i int
for i <= 10 {
i++
}

上述代码其实类似于其他编程语言中的 while,在 while 后添加一个条件表达式。

3、键值循环结构(for range)

3.1、标准循环

在许多情况下都非常有用,for range 可以遍历数组、切片、字符串、map 及通道(channel),for range 语法上类似于其它语言中的 foreach 语句,一般形式为:

for key, val := range coll { // for pos, char := range str {
...
}

一个字符串是 Unicode 编码的字符(或称之为 rune )集合,因此也可以用它来迭代字符串。每个 rune 字符和索引在 for range 循环中是一一对应的,它能够自动根据 UTF-8 规则识别 Unicode 编码的字符。

通过 for range 遍历的返回值有一定的规律:

  • 数组、切片、字符串返回索引和值。
  • map 返回键和值。
  • 通道(channel)只返回通道内的值。

3.2、遍历数组、切片(获得索引和值)

在遍历代码中,key 和 value 分别代表切片的下标及下标对应的值,数组也是类似的遍历方法:

for key, value := range []int{1, 2, 3, 4} {
fmt.Printf("key:%d value:%d\n", key, value)
}

代码输出如下:

key:0  value:1
key:1 value:2
key:2 value:3
key:3 value:4

3.3、遍历字符串(获得字符)

var str = "hello 你好"
for key, value := range str {
fmt.Printf("key:%d value:0x%x\n", key, value)
}

代码输出如下:

key:0 value:0x68
key:1 value:0x65
key:2 value:0x6c
key:3 value:0x6c
key:4 value:0x6f
key:5 value:0x20
key:6 value:0x4f60
key:9 value:0x597d

代码中的变量 value,实际类型是 rune 类型,以十六进制打印出来就是字符的编码。

3.4、遍历 map(获得 map 的键和值)

m := map[string]int{
"hello": 100,
"world": 200,
}
for key, value := range m {
fmt.Println(key, value)
}

代码输出如下:

hello 100
world 200

对 map 遍历时,遍历输出的键值是无序的,如果需要有序的键值对输出,需要对结果进行排序。

3.5、遍历通道 channel(接收通道数据)

c := make(chan int)
go func() {
c <- 1
c <- 2
c <- 3
close(c)
}()
for v := range c {
fmt.Println(v)
}

结果:

Go开发 之 流程控制(if/else、for/range、switch、goto、break、continue)_for

3.6、在遍历中选择希望获得的变量

m := map[string]int{
"hello": 100,
"world": 200,
}
for _, value := range m { // "_"可以理解为一种占位符,匿名变量本身不会进行空间分配,也不会占用一个变量的名字
fmt.Println(value)
}

代码输出如下:

100
200

再看一个匿名变量的例子:

for key, _ := range []int{1, 2, 3, 4} {
fmt.Printf("key:%d \n", key)
}

代码输出如下:

key:0
key:1
key:2
key:3

4、分支结构(switch / case)

Go语言的 switch 表达式不需要为常量,甚至不需要为整数,case 按照从上到下的顺序进行求值,直到找到匹配的项,如果 switch 没有表达式,则​​对 true​​进行匹配,因此,可以将 if else-if else 改写成一个 switch。

4.1、基本写法

Go语言改进了 switch 的语法设计,case 与 case 之间是独立的代码块,不需要通过 break 语句跳出当前 case 代码块以避免执行到下一行。每个 switch 只能有一个 default 分支。例如:

var a = "hello"
switch a {
case "hello":
fmt.Println(1)
case "world":
fmt.Println(2)
default:
fmt.Println(0)
}

4.2、一分支多值

不同的 case 表达式使用逗号分隔。

var a = "mum"
switch a {
case "mum", "daddy":
fmt.Println("family")
}

4.3、分支表达式

var r int = 11
switch {
case r > 10 && r < 20:
fmt.Println(r)
}

4.4、跨越 case 的 fallthrough(兼容C语言的 case 设计)

var s = "hello"
switch {
case s == "hello":
fmt.Println("hello")
fallthrough
case s != "world":
fmt.Println("world")
}

结果:

hello
world

新编写的代码,不建议使用 fallthrough。

5、跳转结构(break / continue / goto)

5.1、跳出制定循环 break

package main
import "fmt"
func main() {
OuterLoop:
for i := 0; i < 2; i++ {
for j := 0; j < 5; j++ {
switch j {
case 2:
fmt.Println(i, j)
break OuterLoop
case 3:
fmt.Println(i, j)
break OuterLoop
}
}
}
}

结果:

0 2

5.2、继续下一次循环 continue

package main
import "fmt"
func main() {
OuterLoop:
for i := 0; i < 2; i++ {
for j := 0; j < 5; j++ {
switch j {
case 2:
fmt.Println(i, j)
continue OuterLoop
}
}
}
}

结果:

0 2
1 2

5.3、跳转到指定的标签 goto

goto 语句通过标签进行代码间的无条件跳转,同时 goto 语句在快速跳出循环、避免重复退出上也有一定的帮助,使用 goto 语句能简化一些代码的实现过程。

5.3.1、使用 goto 退出多层循环

原始代码:

package main
import "fmt"
func main() {
var breakAgain bool
// 外循环
for x := 0; x < 10; x++ {
// 内循环
for y := 0; y < 10; y++ {
// 满足某个条件时, 退出循环
if y == 2 {
// 设置退出标记
breakAgain = true
// 退出本次循环
break
}
}
// 根据标记, 还需要退出一次循环
if breakAgain {
break
}
}
fmt.Println("done")
}

经过 goto 后优化:

package main
import "fmt"
func main() {
for x := 0; x < 10; x++ {
for y := 0; y < 10; y++ {
if y == 2 {
// 跳转到标签
goto breakHere
}
}
}
// 手动返回, 避免执行进入标签
return
// 标签
breakHere:
fmt.Println("done")
}

使用 goto 语句后,无须额外的变量就可以快速退出所有的循环。

5.3.2、使用 goto 集中处理错误

err := firstCheckError()
if err != nil {
fmt.Println(err)
exitProcess()
return
}
err = secondCheckError()
if err != nil {
fmt.Println(err)
exitProcess()
return
}
fmt.Println("done")

使用 goto 后:

err := firstCheckError()
if err != nil {
goto onExit
}
err = secondCheckError()
if err != nil {
goto onExit
}
fmt.Println("done")
return
onExit:
fmt.Println(err)
exitProcess()

6、示例

6.1、九九乘法表

package main
import "fmt"

var Info string = "沙师弟 -- 九九乘法表"

func main() {
fmt.Println(Info)
fmt.Println()
// 遍历, 决定处理第几行
for y := 1; y <= 9; y++ {
// 遍历, 决定这一行有多少列
for x := 1; x <= y; x++ {
fmt.Printf("%d*%d=%d ", x, y, x*y)
}
// 手动生成回车
fmt.Println()
}
}

结果:

Go开发 之 流程控制(if/else、for/range、switch、goto、break、continue)_if/else_02

6.2、冒泡排序

package main
import "fmt"

var Info string = "沙师弟 -- 冒泡排序"

func main() {
arr := [...]int{11,12,42,83,34,34,47,54}
var n = len(arr)
fmt.Println(Info)
fmt.Println()
fmt.Println("--------没排序前--------\n",arr)
for i := 0; i <= n-1; i++ {
fmt.Println("--------第",i+1,"次冒泡--------")
for j := i; j <= n-1; j++ {
if arr[i] > arr[j] {
t := arr[i]
arr[i] = arr[j]
arr[j] = t
}
fmt.Println(arr)
}
}
fmt.Println("--------最终结果--------\n",arr)
}

结果:

沙师弟 -- 冒泡排序

--------没排序前--------
[11 12 42 83 34 34 47 54]
--------第 1 次冒泡--------
[11 12 42 83 34 34 47 54]
[11 12 42 83 34 34 47 54]
[11 12 42 83 34 34 47 54]
[11 12 42 83 34 34 47 54]
[11 12 42 83 34 34 47 54]
[11 12 42 83 34 34 47 54]
[11 12 42 83 34 34 47 54]
[11 12 42 83 34 34 47 54]
--------第 2 次冒泡--------
[11 12 42 83 34 34 47 54]
[11 12 42 83 34 34 47 54]
[11 12 42 83 34 34 47 54]
[11 12 42 83 34 34 47 54]
[11 12 42 83 34 34 47 54]
[11 12 42 83 34 34 47 54]
[11 12 42 83 34 34 47 54]
--------第 3 次冒泡--------
[11 12 42 83 34 34 47 54]
[11 12 42 83 34 34 47 54]
[11 12 34 83 42 34 47 54]
[11 12 34 83 42 34 47 54]
[11 12 34 83 42 34 47 54]
[11 12 34 83 42 34 47 54]
--------第 4 次冒泡--------
[11 12 34 83 42 34 47 54]
[11 12 34 42 83 34 47 54]
[11 12 34 34 83 42 47 54]
[11 12 34 34 83 42 47 54]
[11 12 34 34 83 42 47 54]
--------第 5 次冒泡--------
[11 12 34 34 83 42 47 54]
[11 12 34 34 42 83 47 54]
[11 12 34 34 42 83 47 54]
[11 12 34 34 42 83 47 54]
--------第 6 次冒泡--------
[11 12 34 34 42 83 47 54]
[11 12 34 34 42 47 83 54]
[11 12 34 34 42 47 83 54]
--------第 7 次冒泡--------
[11 12 34 34 42 47 83 54]
[11 12 34 34 42 47 54 83]
--------第 8 次冒泡--------
[11 12 34 34 42 47 54 83]
--------最终结果--------
[11 12 34 34 42 47 54 83]

6.3、二分查找

package main
import "fmt"

var Info string = "沙师弟 -- 二分查找法"

//二分查找函数 假设有序数组的顺序是从小到大(很关键,决定左右方向)
func BinaryFind(arr *[]int, leftIndex int, rightIndex int, findVal int) {
if leftIndex > rightIndex { //判断 leftIndex是否大于rightIndex
fmt.Println("找不到")
return
}
middle := (leftIndex + rightIndex) / 2 //先找到 中间的下标
if (*arr)[middle] > findVal {
BinaryFind(arr, leftIndex, middle-1, findVal) //要查找的数,范围应该在 leftIndex 到 middle+1
} else if (*arr)[middle] < findVal {
BinaryFind(arr, middle+1, rightIndex, findVal) //要查找的数,范围应该在 middle+1 到 rightIndex
} else {
fmt.Printf("找到了,下标为:%v \n", middle)
}
}
func main() {
fmt.Println(Info)
fmt.Println()
//定义一个数组
arr := []int{2, 3, 6, 8, 11, 22, 31, 36, 39, 54, 67, 76, 80, 81, 85, 91, 94, 98}
BinaryFind(&arr, 0, len(arr) - 1, 36)
fmt.Println("main arr :",arr)
}

结果:

Go开发 之 流程控制(if/else、for/range、switch、goto、break、continue)_go_03

6.4、简易聊天机器人

package main

import (
"bufio"
"fmt"
"os"
"strings"
)

var Info string = "沙师弟 -- 简易聊天机器人"

func main() {
fmt.Println(Info)
fmt.Println()
// 准备从标准输入读取数据。
inputReader := bufio.NewReader(os.Stdin)
fmt.Println("请输入你的名字:")
// 读取数据直到碰到 \n 为止。
input, err := inputReader.ReadString('\n')
if err != nil {
fmt.Printf("异常退出: %s\n", err)
// 异常退出。
os.Exit(1)
} else {
// 用切片操作删除最后的 \n 。
name := input[:len(input)-1]
fmt.Printf("你好, %s! 我能为你做什么?\n", name)
}
for {
input, err = inputReader.ReadString('\n')
if err != nil {
fmt.Printf("异常退出: %s\n", err)
continue
}
input = input[:len(input)-1]
// 全部转换为小写。
input = strings.ToLower(input)
switch input {
case "":
continue
case "拜拜", "bye":
fmt.Println("下次再回!")
// 正常退出。
os.Exit(0)
default:
fmt.Println("对不起,我不清楚你说什么……")
}
}
}

结果:

Go开发 之 流程控制(if/else、for/range、switch、goto、break、continue)_range_04


【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论