本节介绍Go语言中的程序流程控制结构,具体包括以下内容:
- 循环结构
- 流程控制语句
- 条件分支结构
经过前面两个小节的学习,我们初步掌握了Go语言的语法知识。本小节将介绍循环和条件分支,从而使用少量代码完成大量重复性的操作,就像标准化作业一样。为了便于理解,本小节将使用3个示例来讲述具体的知识点。
循环结构
先来看这样一段输出:
可以看到,这是个由星号 * 组成的实心菱形,总共有13行。如何实现这样的效果呢?该不会是手写13行输出语句吧?当然不是!
💡 提示: 在实际开发中,产品的需求千变万化。面对复杂的功能,通常的做法是“拆分”。即把一个复杂的需求分解为多个简单的需求,然后逐个实现。
仔细观察上图中的输出内容可以发现,其实:
- 菱形就是由若干行(总共13行)组成的;
- 菱形是上下轴对称图形,以第7行为轴。
将菱形拆分为上下两个三角形,可以发现:
- 每行都是由若干空格和若干星号构成;
- 对于上半部分(i从1开始):
- 第i行的星号数是2*i-1个;
- 第i行的空格数是7-i个。
- 对于下半部分(i从1开始):
- 第i行的空格数是i个;
- 第i行的星号数是2*7-1-(i*2)个。
到此,我们不仅做完了拆分,还摸透了规律。接下来,只要按规律编写代码即可。在Go语言中,像这种重复地执行相似逻辑,可以使用循环结构实现。循环结构的格式如下:
for init; condition; post {
//循环体代码块
}
其中,for表明接下来的代码是for循环结构;init是初始化语句;condition是关系或逻辑表达式,值为true时则会停止循环;post是每次循环结束后执行的语句;循环体代码块就是要重复执行的代码了。
接下来,我们用这个循环结构实现上半部分的三角形:
func main() {
n := 7
for i := 1; i <= n; i++ {
for j := 0; j < n-i; j++ {
fmt.Print(" ")
}
for k := 0; k < 2*i-1; k++ {
fmt.Print("*")
}
fmt.Println()
}
}
执行结果为:
我们逐行理解上述代码。
- 声明变量n,并赋初值为7,表示上半部分由7行构成(包含对称轴)。
- 接下来是for循环结构,初始化条件是变量i为1,i表示总行数;判断退出循环的条件是变量i小于或等于n,即输出7行后退出;每次循环即完成单行输出,因此在循环后i自增1。如此,便构成了总共循环7次的for循环结构。
- 在for循环内部,有两个for循环,分别用于输出空格和星号,另外在两个循环执行完毕后,输出了换行,用于折行。
- 用于输出空格的for循环结构,初始化条件是变量j为0,j表示输出的空格数;判断退出循环的条件是变量j小于n-i,即总共输出n-i个空格;每次循环完成所有空格的输出,因此循环后j自增1。如此,便实现了单行空格的输出。
- 用于输出星号的for循环结构,初始化条件是变量k为0,k表示输出的星号数;判断退出循环的条件是变量k小于2*i-1,即总共输出2*i-1个星号;每次循环完成所有星号的输出,因此循环后k自增1。如此,便实现了单行星号的输出。
好了,如果各位理解了上半部分的实现原理,不妨亲自动手尝试实现下半部分。
❗️ 注意: 使用循环时,务必确保有明确的可退出循环的条件,否则程序将陷入死循环,无法终止。在开发时,若不慎执行了死循环,可点击GoLand工具栏中的“Stop”按钮(接近红色,有点像音乐播放器的停止播放按钮)强行停止。
输出菱形的完整代码如下:
func main() {
n := 7
for i := 1; i <= n; i++ {
for j := 0; j < n-i; j++ {
fmt.Print(" ")
}
for k := 0; k < 2*i-1; k++ {
fmt.Print("*")
}
fmt.Println()
}
for i := 1; i < n; i++ {
for j := 0; j < i; j++ {
fmt.Print(" ")
}
for k := 0; k < 2*n-1-2*i; k++ {
fmt.Print("*")
}
fmt.Println()
}
}
条件分支结构
接下来,输出菱形的题目要“升级了”!
单纯地输出菱形未免太单调了些,我们希望通过不同的文字来输出不同的样式。当文字为“上”的时候,输出上半截7行三角形;当文字为“下”时,输出下半截6行三角形;当文字为“全”是,输出13行菱形。
显然,这个题目升级需要进行“条件判断”,不同的条件执行不同的逻辑。如果Go语言能有“如果……那么……”这种结构就好了!Go语言恰恰有这种结构——条件分支。
Go语言中的条件分支结构如下:
if condition {
//条件成立时要执行的语句
}else{
//条件不成立时要执行的语句
}
其中,condition是关系或逻辑表达式。另外,如无必要,else是可以省略的:
if condition {
//条件成立时要执行的语句
}
如有必要,还可追加更多的判断条件:
if condition1 {
//条件condition1成立时要执行的语句
}else if condition2 {
//条件condition2成立时要执行的语句
}else if condition3 {
//条件condition3成立时要执行的语句
}else{
//以上三种条件都不成立时要执行的语句
}
如此,解答“升级”后的题目就容易多了。参考条件分支的结构,思路如下:
if 文字是“上”或“全”{
输出菱形前7行
}
if 文字是“下”或“全”{
输出菱形后6行
}
之前我们已经完成了输出菱形,只需将新的代码逻辑与之结合即可,完整代码如下:
func main() {
outputMode := "全"
n := 7
if outputMode == "上" || outputMode == "全" {
for i := 1; i <= n; i++ {
for j := 0; j < n-i; j++ {
fmt.Print(" ")
}
for k := 0; k < 2*i-1; k++ {
fmt.Print("*")
}
fmt.Println()
}
}
if outputMode == "下" || outputMode == "全" {
for i := 1; i < n; i++ {
for j := 0; j < i; j++ {
fmt.Print(" ")
}
for k := 0; k < 2*n-1-2*i; k++ {
fmt.Print("*")
}
fmt.Println()
}
}
}
我们可以通过改变outputMode的值来控制输出的文字形状。
流程控制语句
流程控制语句多用于管理循环结构的运行。考虑这样一个需求:编程实现查找1-10以内的素数。
💡 提示: 素数又称质数,是指在大于1的自然数中,除了1和它本身以外不能被其它整数整除的自然数,2是最小的素数。
这一次,我们需要在已有的代码上增加,但不改变原有的代码。先来看看现有代码:
func main() {
for i := 2; i > 0; i++ {
if i == 2 {
fmt.Println(i)
}
//假定i为素数
flag := true
for j := 2; j < i; j++ {
if i%j == 0 {
//当i能被某个整数整除时,不是素数
flag = false
}
}
//如果依旧为true,则i为素数
if flag {
fmt.Println(i)
}
}
}
通过阅读上述代码可以发现:
- 代码整体由一个for循环构成,初始化语句声明了变量i,从2开始(2是最小的素数),循环结束的条件是i大于0,每次循环结束后i自增1;
- 循环体内,首先判断了i是否等于2,如果是的话直接输出了i的值;
- 然后,声明了布尔类型变量flag,表明是否为素数,用于后续判断是否输出i的值;
- 接下来,使用循环结构判断i是否为素数。初始化时声明了变量j,从2开始,跳出循环的条件时j小于i,判断i是否为素数只需从2开始尝试做除法,直到i-1为止。若余数为0,则表示能被整除,此时,flag应改为false。每次循环结束后j自增1;
- 最后,判断flag的值,若flag为true,则表示i是素数,输出i,反之则不是素数。
代码运行后,控制台输出:
2
2
3
5
7
11
13
...
明明是查找10以内的素数,为何不停地输出这么多结果呢?请大家来找茬,看看这段代码中有哪些问题?
- 最外层的for循环,终止条件是i大于0,但i始终是大于0的,程序一旦开始,便无法结束,陷入死循环;
- 当i等于2时,输出了一次i的值。然而在内层的循环体中,还将再次输出。最终将输出两次2;
- 在内层循环中,一旦i与j取余结果为0,则表明i不是素数,内层for循环结构无需再执行剩下的循环了。
解决了这3个问题,便能实现查找1-10以内的素数的需求了。要解决它们,就需要请出Go语言中的流程控制语句来“救场”了。在Go语言中,较为常用的流程控制语句有continue和break。前者的意义是立即结束本次循环,执行下一个循环;后者的意义是终止循环。
显然,解决问题1和3,只需使用break语句打断相应循环的执行即可;解决问题2,只需使用continue语句提前终止本次循环,直接执行下一次循环即可。因此,将代码改为:
func main() {
for i := 2; i > 0; i++ {
//当i大于10s
if i > 10 {
break
}
if i == 2 {
fmt.Println(i)
continue
}
//假定i为素数
flag := true
for j := 2; j < i; j++ {
if i%j == 0 {
//当i能被某个整数整除时,不是素数
flag = false
break
}
}
//如果依旧为true,则i为素数
if flag {
fmt.Println(i)
}
}
}
运行结果为:
2
3
5
7
小结
🎉 恭喜,您完成了本次课程的学习!
📌 以下是本次课程的重点内容总结:
- 循环结构
- 流程控制语句
- 条件分支结构
在实际开发中,代码流程控制可以帮助我们使用较少的代码完成大量重复性的工作。除了本讲中提及的常用流程控制结构外,还有用于条件分支的Switch...case...结构、用于流程控制的goto语句,感兴趣的朋友可以当作扩展阅读。
➡️ 在下次课程中,我会介绍如下内容:
- Go语言中的数组、切片和集合的声明和赋值
零基础通关 Go 语言 - 萧文翰 - 掘金小册2大部分,10大技能,全解Go语言语法。「零基础通关 Go 语言」由萧文翰撰写,950人购买https://s.juejin.cn/ds/kqXRu6Y/