因为某些情况下需要重复执行操作,因此就会有需要重复利用到的语句,就出现了循环

# for 循环

go 语言有三种形式的 for 循环

1
2
3
for init; condition; post { }
for condition { }
for { }

三种形式都在 c 语言中有相对应的形式,第一种和 c 语言中的 for 循环一样,第二种则是与 c 语言中中 while 循环一样,第三种和 c 语言的 for (;😉
一样
其中第一种形式中的 init;condition;post 都代表着特定的书写,如下:

1
2
3
init: 一般为赋值表达式,给控制变量赋初值;
condition: 关系表达式或逻辑表达式,循环控制条件;
post: 一般为赋值表达式,给控制变量增量或减量。

for 循环的执行过程和其它编程语言一样,首先先赋初值,接着 condition 判断是否满足条件,满足则继续执行循环内的语句,结束之后再执行 post 表达式,进入下一个循环,判断 condition,如果为假则跳出循环
for 循环的 range 格式可以对
slice、map、数组、字符串等进行迭代循环,如下:

1
2
3
for key, value := range oldMap {
newMap[key] = value
}

以上的 key 和 value 可忽略
如,只想读取 key,可以如下书写:

1
for key := range oldMap

也可以

1
for key, _ := range oldMap

只想读取 value 同理:

1
for _, value := range oldMap

# 实例 1:

接下来用 for 循环简单编写一个计算 1-100 的和的代码

1
2
3
4
5
6
7
8
9
10
11
package main

import "fmt"

func main() {
    sum := 0
    for i := 0; i <= 100; i++ {
        sum += i
    }
    fmt.Println(sum)
}

输出:

当然如上文所说用 c 语言的 while 格式也可以,如下:

# 实例 2:

计算 sum 自加的值

1
2
3
4
5
6
7
8
9
10
11
package main

import "fmt"

func main() {
    sum := 1
    for sum <= 10 {
        sum += sum
    }
    fmt.Println(sum)
}

输出:

注意这里计算 sum 自加的值,sum 的初始值不能为 0,不然一直是 0 自加永远满足条件无法跳出循环进行输出

# 实例 3:

For-each range 循环

这种格式的循环可以对字符串、数组、切片等进行迭代输出元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import (
    "fmt"
)

func main() {
    strings := []string{"nnnpc", "good"}
    for i, s := range strings {
        fmt.Println(i, s)
    }

    strings1 := [6]string{"n", "n", "n", "p", "c"}
    for i, x := range strings1 {
        fmt.Printf("第 %d 位 x 的值 = %d\n", i, x)
    }
}

输出:

以上代码可以大体分为两块,第一块是变量 strings,默认赋给了变量两个空间,分别用字符串 nnnpc 和 good 占用,利用 range 将 strings 中的 key 值和 value 值迭代赋值给变量 i 和 s,并且循环将 i,s 输出。第二块则是变量 strings1,确定了六个空间给 strings1,并且五个空间分别用字母占用,同样的将 key 值和 value 值赋给了 i,x,使用 range 来进行迭代循环输出

如上文所述,可以省略 key 或者 value,只输出自己想要的,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package main

import "fmt"

func main() {
    map1 := make(map[int]float32)
    map1[1] = 1.0
    map1[2] = 2.0
    map1[3] = 3.0
    map1[4] = 4.0

    for key, value := range map1 {
        fmt.Printf("key is: %d - value is: %f\n", key, value)
    }

    for key := range map1 {
        fmt.Printf("key is: %d\n", key)
    }
    fmt.Printf("=================省略value也可如下书写=====================\n")
    for key, _ := range map1 {
        fmt.Printf("key is: %d\n", key)
    }
    fmt.Printf("==========================================================\n")
    for _, value := range map1 {
        fmt.Printf("value is: %f\n", value)
    }
}

输出:

第一个 for 循环二者均输出,第二、三个只输出 key,第四个只输出 value

# 循环嵌套

顾名思义就是在循环内嵌套循环

语法:

1
2
3
4
5
6
7
8
for [condition |  ( init; condition; increment ) | Range]
{
   for [condition |  ( init; condition; increment ) | Range]
   {
      statement(s);
   }
   statement(s);
}

# 实例:

输出 2-100 之间的素数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main
import "fmt"
func main() {
   var i, j int
   for i=2; i < 100; i++ {
      for j=2; j <= (i/j); j++ {
         if(i%j==0) {
            break; // 如果发现因子,则不是素数
         }
      }
      if(j > (i/j)) {
         fmt.Printf("%d  是素数\n", i);
      }
   }  
}

输出:

# 循环控制语句

循环控制语句可以控制循环体内语句的执行过程。

# break 语句

在 Go 语言中,break 语句用于终止当前循环或者 switch
语句的执行,并跳出该循环或者 switch 语句的代码块。
break 语句可以用于以下几个方面:
1. 用于循环语句中跳出循环,并开始执行循环之后的语句。
2.break 在 switch 语句中在执行一条 case 后跳出语句的作用。
3.break 可应用在 select 语句中。
4. 在多重循环中,可以用标号 label 标出想 break 的循环。

实例 1:在 for 循环中使用 break 语句

1
2
3
4
5
6
7
8
9
10
package main
import "fmt"
func main() {
    for i := 0; i < 10; i++ {
        if i == 5 {
            break
        }
        fmt.Println(i)
    }
}

输出:

以上简单的 break 程序当 i 等于 5 的时候跳出循环

接着说明使用标记与不使用标记的区别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main
import "fmt"
func main() {
   // 不使用标记
   fmt.Println("---- break ----")
   for i := 1; i <= 3; i++ {
      fmt.Printf("i: %d\n", i)
      for i2 := 11; i2 <= 13; i2++ {
         fmt.Printf("i2: %d\n", i2)
         break
      }
   }
   // 使用标记
   fmt.Println("---- break label ----")
   re:
      for i := 1; i <= 3; i++ {
         fmt.Printf("i: %d\n", i)
         for i2 := 11; i2 <= 13; i2++ {
         fmt.Printf("i2: %d\n", i2)
         break re
      }
   }
}

输出:

添加标记能 break 掉标记指定的循环语句,而没有标记只能 break 当前 break 所在的循环

实例 2:在 switch 语句中使用 break

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main
import "fmt"
func main() {
    day := "Tuesday"
    switch day {
    case "Monday":
        fmt.Println("It's Monday.")
    case "Tuesday":
        fmt.Println("It's Tuesday.")
        break
    case "Wednesday":
        fmt.Println("It's Wednesday.")
    }
}

刚开始给 day 声明赋值为 Tuesday 而 switch 语句中 case 也是 Tuesday 时执行 break,也就是输出 It's
Tuesday 并跳出 switch 语句
输出:

实例 3:在 select 语句中使用 break

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main
import (
    "fmt"
    "time"
)
func main() {
    ch1 := make(chan int)
    ch2 := make(chan int)
    go func() {
        time.Sleep(2 * time.Second)
        ch1 <- 1
    }()
    go func() {
        time.Sleep(1 * time.Second)
        ch2 <- 2
    }()
    select {
    case <-ch1:
        fmt.Println("Received from ch1.")
    case <-ch2:
        fmt.Println("Received from ch2.")
        break // 跳出 select 语句
    }
}

输出:

在 Go 语言中,break 语句在 select 语句中的应用是相对特殊的。由于
select 语句的特性,break 语句并不能直接用于跳出 select 语句本身,因为
select
语句是非阻塞的,它会一直等待所有的通信操作都准备就绪。如果需要提前结束
select 语句的执行,可以使用 return 或者 goto 语句来达到相同的效果。
由于 select 语句是非阻塞的,因此 braek 不能直接跳出 select 语句本身,可以使用 return 和 goto 来达到提前结束 select 语句的效果,如下

return:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package main
import (
    "fmt"
    "time"
)
func process(ch chan int) {
    for {
        select {
        case val := <-ch:
            fmt.Println("Received value:", val)
            if val == 5 {
                return
            }
        default:
            fmt.Println("No value received yet.")
            time.Sleep(500 * time.Millisecond)
        }
    }
}
func main() {
    ch := make(chan int)
    go process(ch)
    time.Sleep(2 * time.Second)
    ch <- 1
    time.Sleep(1 * time.Second)
    ch <- 3
    time.Sleep(1 * time.Second)
    ch <- 5
    time.Sleep(1 * time.Second)
    ch <- 7
    time.Sleep(2 * time.Second)
}

输出:

可以看到整个程序段最终接收到 7,也就是 val==7,但由于在循环中当接收到的变量值为 5 时进行了 return 操作,效果相当于跳出了 select 语句,也就不会进行后续的接收 7 的操作了

通过使用 return,我们可以在 select
语句中提前终止执行,并返回到调用者的代码中。
需要注意的是,使用 return
语句会立即终止当前的函数执行,所以请根据实际需求来决定在 select
语句中使用何种方式来提前结束执行。

# continue 语句

Go 语言的 continue 语句 有点像 break 语句。但是 continue
不是跳出循环,而是跳过当前循环执行下一次循环语句。
for 循环中,执行 continue 语句会触发 for 增量语句的执行。
在多重循环中,可以用标号 label 标出想 continue 的循环。

通俗的来讲 continue 的作用就是跳过本次循环执行下一次循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main
import "fmt"
func main() {
   /* 定义局部变量 */
   var a int = 10
   /* for 循环 */
   for a < 20 {
      if a == 15 {
         /* 跳过此次循环 */
         a = a + 1;
         continue;
      }
      fmt.Printf("a 的值为 : %d\n", a);
      a++;    
   }  
}

输出:

该程序当 a 的值为 15 时就跳过该次循环,也就是不执行输出和自加,通过 a=a+1 之后执行下一次的循环,因此输出里面没有值为 15

接下来记录一下使用和不使用标记的区别:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main
import "fmt"
func main() {
    // 不使用标记
    fmt.Println("---- continue ---- ")
    for i := 1; i <= 3; i++ {
        fmt.Printf("i: %d\n", i)
            for i2 := 11; i2 <= 13; i2++ {
                fmt.Printf("i2: %d\n", i2)
                continue
            }
    }
    // 使用标记
    fmt.Println("---- continue label ----")
    re:
        for i := 1; i <= 3; i++ {
            fmt.Printf("i: %d\n", i)
                for i2 := 11; i2 <= 13; i2++ {
                    fmt.Printf("i2: %d\n", i2)
                    continue re
                }
        }
}

输出:

可以明显看到没用标记之前每个 i 对应三个 i2,使用之后每个 i 对应一个 i2。正是因为标记 re 在整个大 for 循环外面,当执行一次循环之后直接跳出了本次的大 for 循环,回到了起始位置。而没有标记的只是正常结束之后跳出本次的小 for 循环,进行下一次,直到执行三次后不满足小 for 循环条件之后跳回大 for 循环

# goto 语句

Go 语言的 goto 语句可以无条件地转移到过程中指定的行。
goto 语句通常与条件语句配合使用。可用来实现条件转移,
构成循环,跳出循环体等功能。
但是,在结构化程序设计中一般不主张使用 goto 语句,
以免造成程序流程的混乱,使理解和调试程序都产生困难。

简单来说 goto 可以从一代码无条件跳转到另一代码进行执行

语法:

1
2
3
4
goto label;
..
.
label: statement;

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main

import "fmt"

func main() {
    /* 定义局部变量 */
    var a int = 10

    /* 循环 */
LOOP:
    for a < 20 {
        if a == 15 {
            /* 跳过迭代 */
            a = a + 1
            goto LOOP
        }
        fmt.Printf("a的值为 : %d\n", a)
        a++
    }
}

输出:

这个和前面的 continue 标记执行效果相同,但是实际作用是不一样的,当 a 的值为 15 时执行 a=a+1 之后直接去到 LOOP 所在的地方不执行输出和自加,而这里 LOOP 恰好就是下一次的循环开始,因此结果中没有输出值为 15 的

更新于