go基础:流程控制

164 阅读6分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第7天,点击查看活动详情

程序设计语言的流程控制语句,用于设定计算执行的次序,建立程序的逻辑结构。可以说,流程控制语句是整个程序的骨架。

从根本上讲,流程控制只是为了控制程序语句的执行顺序,一般需要与各种条件配合,因此,在各种流程中,会加入条件判断语句。流程控制语句一般起以下3个作用:

  • 选择,即根据条件跳转到不同的执行序列;
  • 循环,即根据条件反复执行某个序列,当然每一次循环执行的输入输出可能会发生变化;
  • 跳转,即根据条件返回到某执行序列。

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

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

if/else

关键字 if 是用于测试某个条件(布尔型或逻辑型)的语句,如果该条件成立,则会执行 if 后由大括号{}括起来的代码块,否则就忽略该代码块继续执行后续的代码。

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

关于条件语句,需要注意以下几点:

  • 条件语句不需要使用括号将条件包含起来();
  • 无论语句体内有几条语句,花括号{}都是必须存在的;
  • 左花括号{必须与if或者else处于同一行;
  • 在if之后,条件语句之前,可以添加变量初始化语句,使用;间隔;

for

与多数语言不同的是,Go语言中的循环语句只支持 for 关键字,而不支持 while 和 do-while 结构,关键字 for 的基本使用方法与C语言和 C++非常接近:

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

因为没有while,Go让扩展了for的用法,让开发者不用写无聊的 for(;;){}do{} while(1);,而直接简化为如下的写法:

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

在条件表达式中也支持多重赋值:

a := []int{1, 2, 3, 4, 5, 6}
for i, j := 0, len(a) – 1; i < j; i, j = i + 1, j – 1 {
    a[i], a[j] = a[j], a[i]
}

使用循环语句时,需要注意的有以下几点。

  • 左花括号{必须与for处于同一行。
  • Go语言中的for循环与C语言一样,都允许在循环条件中定义和初始化变量,唯一的区别 是,Go语言不支持以逗号为间隔的多个赋值语句,必须使用平行赋值的方式来初始化多 个变量。
  • Go语言的for循环同样支持continue和break来控制循环,但是它提供了一个更高级的 break,可以选择中断哪一个循环,如下示例,break直接跳出两个for循环:
package main
​
import "fmt"func main() {
JLoop:    
    for j := 0; j < 5; j++ {
        for i := 0; i < 10; i++ {
            if i > 5 {
                break JLoop
            }
            fmt.Println(i)
        }
    }
​
}

for range

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

// Map
for key, value := range oldMap {
    newMap[key] = value
}
// string
for pos, char := range str {
    fmt.Printf("pos:%d  char:%d\n", pos, char)
}
// array
for idx, value := range array {
    fmt.Printf("idx:%d  value:%d\n", idx, value)
}
// chan
for value := range channel {
    fmt.Println(value)
}

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

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

占位符

在使用 for range 循环遍历某个对象时,一般不会同时需要 key 或者 value,这个时候可以采用一些技巧,让代码变得更简单,下面将前面的例子修改一下,参考下面的代码示例:

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

在上面的例子中将 key 变成了下划线_,这里的下划线就是匿名变量。

  • 可以理解为一种占位符。
  • 匿名变量本身不会进行空间分配,也不会占用一个变量的名字。
  • 在 for range 可以对 key 使用匿名变量,也可以对 value 使用匿名变量。

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

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

在该例子中,value 被设置为匿名变量,只使用 key,而 key 本身就是切片的索引,所以例子输出索引。

我们总结一下 for 的功能:

  • Go语言的 for 包含初始化语句、条件表达式、结束语句,这 3 个部分均可缺省。
  • for range 支持对数组、切片、字符串、map、通道进行遍历操作。
  • 在需要时,可以使用匿名变量对 for range 的变量进行选取。

switch/case

根据传入条件的不同,选择语句会执行不同的语句。Go 编程语言中 switch 语句的语法如下:

switch var1 {
    case val1, val11, val111:
        ...
    case val2:
        ...
    default:
        ...
}

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

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

package main
​
import "fmt"func main() {
   /* 定义局部变量 */
   var grade string = "B"
   var marks int = 90
​
   switch marks {
      case 90: grade = "A"
      case 80: grade = "B"
      case 50,60,70 : grade = "C"
      default: grade = "D"  
   }
​
   // switch后面的表达式甚至不是必需
   switch {
      case grade == "A" :
         fmt.Printf("优秀!\n" )    
      case grade == "B", grade == "C" :
         fmt.Printf("良好\n" )      
      case grade == "D" :
         fmt.Printf("及格\n" )      
      case grade == "F":
         fmt.Printf("不及格\n" )
      default:
         fmt.Printf("差\n" );
   }
   fmt.Printf("你的等级是 %s\n", grade );      
}

另外,case 后不仅仅只是常量,还可以和 if 一样添加表达式,代码如下:

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

在使用switch结构时,我们需要注意以下几点:

  • 左花括号{必须与switch处于同一行;
  • 条件表达式不限制为常量或者整数;
  • 单个case中,可以出现多个结果选项;
  • 与C语言等规则相反,Go语言不需要用break来明确退出一个case;
  • 只有在case中明确添加fallthrough关键字,才会继续执行紧跟的下一个case;
  • 可以不设定switch之后的条件表达式,在此种情况下,整个switch结构与多个if...else...的逻辑作用等同。

select

select 是 Go 中的一个控制结构,类似于用于通信的 switch 语句。每个 case 必须是一个通信操作,要么是发送要么是接收。

select 随机执行一个可运行的 case。如果没有 case 可运行,它将阻塞,直到有 case 可运行。一个默认的子句应该总是可运行的。

select {
    case communication clause  :
       statement(s);      
    case communication clause  :
       statement(s);
    /* 可以定义任意数量的 case */
    default : /* 可选 */
       statement(s);
}

前面我们还没讲过通道,这个知识还是比较复杂的,我们后面再单独用一篇内容来讲,这里就先占个坑吧。

goto

goto语句被多数语言学者所反对,谆谆告诫不要使用。但对于Go语言这样一个惜关键字如金的语言来说,居然仍然支持goto关键字,无疑让某些人跌破眼镜。但就个人一年多来的Go语言编程经验来说,goto还是会在一些场合下被证明是最合适的。 goto语句的语义非常简单,就是跳转到本函数内的某个标签,如:

package main

import "fmt"

func myfunc() {
	i := 0
HERE:
	fmt.Println(i)
	i++
	if i < 10 {
		goto HERE
	}
}

func main() {
    myfunc()
}