携手创作,共同成长!这是我参与「掘金日新计划 · 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()
}