前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >11.Go语言-流程控制

11.Go语言-流程控制

原创
作者头像
面向加薪学习
发布2022-09-04 10:40:11
5100
发布2022-09-04 10:40:11
举报
文章被收录于专栏:面向加薪学习面向加薪学习

11. 流程控制

所谓流程控制就是指“程序怎么执行”或者说“程序执行的顺序”。程序整体上确实是从上往下执行,但又不单纯是从上往下。

流程控制可分为三类:

  1. 顺序执行。这个非常简单,就是先执行第一行再执行第二行……这样依次从上往下执行。
  2. 选择执行。有些代码可以跳过不执行,有选择地执行某些代码。
  3. 循环执行。有些代码会反复执行。

11.1 条件语句

在 Go 中 条件语句模型 如下:

代码语言:go
复制
if 条件1 {
  逻辑代码1
} else if  条件2 {
  逻辑代码2
} else if 条件 ... {
  逻辑代码 ...
} else {
  逻辑代码 else
}

如果分支的 condition 为真,则执行该分支 {} 之间的代码。在 Go 中,对于 {} 的位置有严格的要求,它要求 else if (或 else ) 和两边的花括号,必须在同一行。特别注意,即使在 {} 之间只有一条语句,这两个花括号也是不能省略的。

  • 单分支判断

只有一个 if 为单分支判断:

代码语言:go
复制
  score := 88
  if score >= 60 {
      fmt.Println("成绩及格")
  }
  • 双分支判断

if 和一个 else 为两分支判断:

代码语言:go
复制
  score := 88
  if score >= 60 {
      fmt.Println("成绩及格")
  } else {
      fmt.Println("成绩不及格")
  }
  • 多分支判断

ifelse if 以及 else 为多分支判断:

代码语言:go
复制
  score := 88
  if score >= 90 {
      fmt.Println("成绩等级为A")
  } else if score >= 80 {
      fmt.Println("成绩等级为B")
  } else if score >= 70 {
      fmt.Println("成绩等级为C")
  } else if score >= 60 {
      fmt.Println("成绩等级为D")
  } else {
      fmt.Println("成绩等级为E 成绩不及格")
  }
  • 条件语句高级写法

if 还有另外一种写法,它包含一个 statement 可选语句部分,该可选语句在条件判断之前运行。它的语法是:

代码语言:go
复制
  if statement; condition {
  }

上面单分支判断的那个例子可以重写如下:

代码语言:go
复制
  if score := 88; score >= 60 {
      fmt.Println("成绩及格")
  }

11.2 选择语句

在 Go 选择语句模型 如下:

代码语言:go
复制
switch 表达式 {
    case 表达式值1:
        业务逻辑代码1
    case 表达式值2:
        业务逻辑代码2
    case 表达式值3:
        业务逻辑代码3
    case 表达式值 ...:
        业务逻辑代码 ...
    default:
        业务逻辑代码
}

switch 语句是一个选择语句,用于将 switch 后的表达式的值与可能匹配的选项 case 后的表达式进行比较,并根据匹配情况执行相应的代码块,执行完匹配的代码块后,直接退出 switch-case 。如果没有任何一个匹配,就会执行 default 的代码块。它可以被认为是替代多个 if-else 子句的常用方式。注意:case 不允许出现重复项。例如,下面的例子会输出 Your score is between 80 and 90.

代码语言:go
复制
grade := "B"
switch grade {
case "A":
    fmt.Println("Your score is between 90 and 100.")
case "B":
    fmt.Println("Your score is between 80 and 90.")
case "C":
    fmt.Println("Your score is between 70 and 80.")
case "D":
    fmt.Println("Your score is between 60 and 70.")
default:
    fmt.Println("Your score is below 60.")
}
  • 一个 case 多个条件

在 Go 中, case 后可以接多个条件,多个条件之间是 的关系,用逗号 , 相隔。

代码语言:go
复制
  month := 5
  switch month {
  case 1, 3, 5, 7, 8, 10, 12:
      fmt.Println("该月份有 31 天")
  case 4, 6, 9, 11:
      fmt.Println("该月份有 30 天")
  case 2:
      fmt.Println("该月份闰年为 29 天,非闰年为 28 天")
  default:
      fmt.Println("输入有误!")
  }
  • 选择语句高级写法

switch 还有另外一种写法,它包含一个 statement 可选语句部分,该可选语句在表达式之前运行。它的语法是:

代码语言:go
复制
  switch statement; expression {
  }

可以将上面的例子改写为:

代码语言:go
复制
  switch month := 5; month {
  case 1, 3, 5, 7, 8, 10, 12:
      fmt.Println("该月份有 31 天")
  case 4, 6, 9, 11:
      fmt.Println("该月份有 30 天")
  case 2:
      fmt.Println("该月份闰年为 29 天,非闰年为 28 天")
  default:
      fmt.Println("输入有误!")
  }

这里 month 变量的作用域就仅限于这个 switch 内。

  • switch 后可接函数

switch 后面可以接一个函数,只要保证 case 后的值类型与函数的返回值一致即可。

代码语言:go
复制
  package main

  import "fmt"

  func getResult(args ...int) bool {
   for _, v := range args {
    if v < 60 {
     return false
    }
   }
   return true
  }

  func main() {
   chinese := 88
   math := 90
   english := 95

   switch getResult(chinese, math, english) {
   case true:
    fmt.Println("考试通过")
   case false:
    fmt.Println("考试未通过")
   }
  }
  • 无表达式的 switch

switch 后面的表达式是可选的。如果省略该表达式,则表示这个 switch 语句等同于 switch true ,并且每个 case 表达式都被认定为有效,相应的代码块也会被执行。

代码语言:go
复制
  score := 88
  switch {
  case score >= 90 && score <= 100:
      fmt.Println("grade A")
  case score >= 80 && score < 90:
      fmt.Println("grade B")
  case score >= 70 && score < 80:
      fmt.Println("grade C")
  case score >= 60 && score < 70:
      fmt.Println("grade D")
  case score < 60:
      fmt.Println("grade E")
  }

switch-case 语句相当于 if-elseif-else 语句。

  • fallthrough 语句

正常情况下 switch-case 语句在执行时只要有一个 case 满足条件,就会直接退出 switch-case ,如果一个都没有满足,才会执行 default 的代码块。不同于其他语言需要在每个 case 中添加 break 语句才能退出。使用 fallthrough 语句可以在已经执行完成的 case 之后,把控制权转移到下一个 case 的执行代码中。fallthrough 只能穿透一层,不管你有没有匹配上,都要退出了。fallthrough 语句是 case 子句的最后一个语句。如果它出现在了 case 语句的中间,编译会不通过。

代码语言:go
复制
  s := "从0到Go语言微服务架构师"
  switch {
  case s == "从0到Go语言微服务架构师":
      fmt.Println("从0到Go语言微服务架构师")
      fallthrough
  case s == "Go语言微服务架构核心22讲":
      fmt.Println("Go语言微服务架构核心22讲")
  case s != "Go语言极简一本通":
      fmt.Println("Go语言极简一本通")
  }

11.3 循环语句

循环语句 可以用来重复执行某一段代码。在 C 语言中,循环语句有 forwhiledo while 三种循环。但在 Go 中只有 for 一种循环语句。下面是 for 循环语句的四种基本模型:

代码语言:go
复制
// for 接三个表达式
for initialisation; condition; post {
   code
}

// for 接一个条件表达式
for condition {
   code
}

// for 接一个 range 表达式
for range_expression {
   code
}

// for 不接表达式
for {
   code
}

接下来我们对每一种模型进行讲解。

  • 接一个条件表达式

下面的例子利用 for 循环打印 03 的数值:

代码语言:go
复制
  num := 0
  for num < 4 {
      fmt.Println(num)
      num++
  }
  • 接三个表达式

for 后面接的这三个表达式,各有各的用途:

  • 第一个表达式(initialisation):初始化控制变量,在整个循环生命周期内,只执行一次;
  • 第二个表达式(condition):设置循环控制条件,该表达式值为 true 时循环,值为 false 时结束循环;
  • 第三个表达式(post):每次循环完都会执行此表达式,可以利用其让控制变量增量或减量。

这三个表达式,使用 ; 分隔。

代码语言:go
复制
  for num := 0; num < 4; num++ {
      fmt.Println(num)
  }

该程序的输出和上面的例子是等价的。这里注意一点,在第一个表达式声明的变量 num 的作用域只在 for 循环里面有效。

  • 接一个 range 表达式

在 Go 中遍历一个可迭代的对象一般使用 for-range 语句实现,其中 range 后面可以接数组、切片、字符串等, range 会返回两个值,第一个是索引值,第二个是数据值。

代码语言:go
复制
  str := "从0到Go语言微服务架构师"
  for index, value := range str{
      fmt.Printf("index %d, value %c\n", index, value)
  }
  • 不接表达式

for 后面不接表达式就相当于无限循环,当然,可以使用 break 语句退出循环。

下面两种无限循环的写法等价,但一般使用第一种写法。

代码语言:go
复制
  // 第一种写法
  for {
      code
  }
  // 第二种写法
  for ;; {
      code
  }
  • break 语句

break 语句用于终止 for 循环,之后程序将执行在 for 循环后的代码。上面的例子已经演示了 break 语句的使用。

  • continue 语句

continue 语句用来跳出 for 循环中的当前循环。在 continue 语句后的所有的 for 循环语句都不会在本次循环中执行,执行完 continue 语句后将会继续执行一下次循环。下面的程序会打印出 10 以内的奇数。

代码语言:go
复制
  for num := 1; num <= 10; num++ {
      if num % 2 == 0 {
          continue
      }
      fmt.Println(num)
  }

11.4 defer 延迟调用

含有 defer 语句的函数,会在该函数将要返回之前,调用另一个函数。简单点说就是 defer 语句后面跟着的函数会延迟到当前函数执行完后再执行。

代码语言:go
复制
package main

import "fmt"

func bookPrint() {
    fmt.Println("Go语言极简一本通")
}

func main() {
defer bookPrint()
    fmt.Println("main函数...")
}

首先,执行 main 函数,因为 bookPrint() 函数前有 defer 关键字,所以会在执行完 main 函数后再执行 bookPrint() 函数,所以先打印出 main函数... ,再执行 bookPrint() 函数打印 Go语言极简一本通

关于 defer 有几个注意点,下面依次介绍:

  • 即时求值的变量快照

使用 defer 只是延时调用函数,传递给函数里的变量,不应该受到后续程序的影响。

代码语言:go
复制
  str := "Go语言极简一本通"
  defer fmt.Println(str)
  str = "欢喜"
  fmt.Println(str)
  • 延迟方法

defer 不仅能够延迟函数的执行,也能延迟方法的执行。

代码语言:go
复制
  package main

  import "fmt"

  type Book struct {
  	bookName, authorName string
  }

  func (b Book) printName() {
  	fmt.Printf("%s %s", b.bookName, b.authorName)
  }

  func main() {
  	book := Book{"《Go语言极简一本通》", "欢喜"}
  	defer book.printName()
  	fmt.Printf("main... ")
  }
  • defer 栈

当一个函数内多次调用 defer 时,Go 会把 defer 调用放入到一个栈中,随后按照 后进先出 的顺序执行。

代码语言:go
复制
  package main

  import "fmt"

  func main() {
  	defer fmt.Printf("从0到Go语言微服务架构师")
  	defer fmt.Printf("Go语言微服务架构核心22讲")
  	defer fmt.Printf("《Go语言极简一本通》")
  	fmt.Printf("main...")
  }
  • defer 在 return 后调用
代码语言:go
复制
package main

import "fmt"

var s string = "Go语言微服务架构核心22讲"

func showLesson() string {
    defer func() {
        s = "从0到Go语言微服务架构师"
    }()
    fmt.Println("showLesson: s =", s)
    return s
}

func main() {
    lesson := showLesson()
    fmt.Println("main: s =", s)
    fmt.Println("main: lesson =", lesson)
}
  • defer 可以使代码更简洁

如果没有使用 defer ,当在一个操作资源的函数里调用多个 return 时,每次都得释放资源,你可能这样写代码:

代码语言:go
复制
  func f() {
      r := getResource()  //0,获取资源
      ......
      if ... {
          r.release()  //1,释放资源
          return
      }
      ......
      if ... {
          r.release()  //2,释放资源
          return
      }
      ......
      if ... {
          r.release()  //3,释放资源
          return
      }
      ......
      r.release()     //4,释放资源
      return
  }

有了 defer 之后,你可以简洁地写成下面这样:

代码语言:go
复制
  func f() {
      r := getResource()  //0,获取资源

      defer r.release()  //1,释放资源
      ......
      if ... {
          ...
          return
      }
      ......
      if ... {
          ...
          return
      }
      ......
      if ... {
          ...
          return
      }
      ......
      return
  }

11.5 goto 无条件跳转

在 Go 语言中保留 gotogoto 后面接的是标签,表示下一步要执行哪里的代码。

代码语言:go
复制
goto label
...
label: code

下面是使用 goto 的例子:

代码语言:go
复制
package main

import "fmt"

func main() {
	fmt.Println("从0到Go语言微服务架构师")
	goto label
	fmt.Println("Go语言微服务架构核心22讲")
label:
    fmt.Println("《Go语言极简一本通》")
}

goto 语句与标签之间不能有变量声明,否则编译错误。编译下面的程序会报错:

代码语言:go
复制
package main

import "fmt"

func main() {
	fmt.Println("从0到Go语言微服务架构师")
	goto label
	fmt.Println("Go语言微服务架构核心22讲")
	var x int = 0
label:
    fmt.Println("《Go语言极简一本通》")
}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 11. 流程控制
    • 11.1 条件语句
      • 11.2 选择语句
        • 11.3 循环语句
          • 11.4 defer 延迟调用
            • 11.5 goto 无条件跳转
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档