介绍
在计算机编程中,一个 loop 是一个代码结构,循环周围重复执行一个代码片段,通常直到满足某些条件。在计算机编程中使用循环可以让你自动和重复类似的任务多次。
在Go中, " for " 循环执行基于循环计数器或循环变量的重复执行代码。 不同于其他具有多回路构造的编程语言,如"thile","do"等. 只有"为"循环。 这使得您的代码更加清晰和易读,因为您不需要担心实现相同的循环构造的多种策略. 这种增强的可读性和在开发过程中减少的认知负载也会使您的代码比其他语言更容易出错.
在本教程中,您将了解Go的)。
声明 ForClause 和条件循环
为了解释各种用例,在Go中有三种不同的方式来创建for
循环,每个循环都有自己的能力,这些循环是用 Condition 、** ForClause** 或** RangeClause** 创建一个for
循环。
让我们先看看如何使用for
循环与ForClause。
一个 ForClause loop 被定义为具有一个 initial statement,然后是 condition,然后是 post statement。
1for [ Initial Statement ] ; [ Condition ] ; [ Post Statement ] {
2 [Action]
3}
为了解释前面的组件的作用,让我们看看使用 ForClause 语法通过指定的值范围增加的for
循环:
1for i := 0; i < 5; i++ {
2 fmt.Println(i)
3}
让我们打破这个循环,识别每个部分。
循环的第一个部分是 i := 0
. 这是最初的陈述:
1for i := 0; i < 5; i++ {
2 fmt.Println(i)
3}
它表示我们正在声明一个名为i
的变量,并将初始值设置为0
。
接下来是条件:
1for i := 0; i < 5; i++ {
2 fmt.Println(i)
3}
在这种情况下,我们表示虽然i
小于5
的值,但循环应该继续循环。
最后,我们有邮件声明:
1for i := 0; i < 5; i++ {
2 fmt.Println(i)
3}
在帖子声明中,我们将循环变量 i
增加一次,每次使用 i++
增量操作员发生迭代。
当我们运行这个程序时,输出看起来像这样:
1[secondary_label Output]
20
31
42
53
64
循环运行5次.最初,它将)`的操作。
注意:在编程中,我们倾向于从0开始,这就是为什么虽然5个数字被打印出来,但它们从0到4之间。
我们不局限于从0开始或以特定值结束,我们可以将任何值分配给我们的初始陈述,也可以停止在我们的帖子陈述中的任何值。
1for i := 20; i < 25; i++ {
2 fmt.Println(i)
3}
在这里,迭代从20(包括)到25(专属),所以输出看起来像这样:
1[secondary_label Output]
220
321
422
523
624
我们还可以使用我们的帖子声明来增加不同的值,这类似于其他语言中的步骤
:
首先,让我们使用一个有正值的帖子陈述:
1for i := 0; i < 15; i += 3 {
2 fmt.Println(i)
3}
在这种情况下,将for
循环设置为从0到15的数字打印出来,但以3的增量打印,所以只有每一个第三个数字打印出来:
1[secondary_label Output]
20
33
46
59
612
我们还可以为我们的帖子陈述参数使用负值来反复反复,但我们必须相应地调整我们的初始陈述和条件参数:
1for i := 100; i > 0; i -= 10 {
2 fmt.Println(i)
3}
在这里,我们将i
设置为100
的初始值,使用i < 0
的条件停在0
时,后续陈述将该值降至-=
运算器的10。
1[secondary_label Output]
2100
390
480
570
660
750
840
930
1020
1110
您也可以从for
语法中排除初始语句和帖子语句,并且只使用条件。
1i := 0
2for i < 5 {
3 fmt.Println(i)
4 i++
5}
这一次,我们将变量i
与前行代码中的for
循环分开声明,循环只有一个条件条款来检查i
是否小于5
。
有时您可能不知道需要完成某项任务的迭代次数;在这种情况下,您可以忽略所有陈述,并使用打破
关键字来退出执行:
1for {
2 if someCondition {
3 break
4 }
5 // do action here
6}
一个例子是,如果我们正在从一个无限大小的结构中阅读(https://golang.org/pkg/bytes/# Buffer),我们不知道什么时候我们将完成阅读:
1[label buffer.go]
2package main
3
4import (
5 "bytes"
6 "fmt"
7 "io"
8)
9
10func main() {
11 buf := bytes.NewBufferString("one\ntwo\nthree\nfour\n")
12
13 for {
14 line, err := buf.ReadString('\n')
15 if err != nil {
16 if err == io.EOF {
17
18 fmt.Print(line)
19 break
20 }
21 fmt.Println(err)
22 break
23 }
24 fmt.Print(line)
25 }
26}
在前面的代码中,buf:=字节. NewBufferString ("一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等一等 因为我们不知道缓冲器何时会完成读取,我们创造了一个没有条款的"为"循环. 在
for' 环内,我们使用`行,错误:= buf.ReadString ('\n')'读取缓冲器的一行并检查是否有从缓冲器读取的错误。 如果有的话,我们处理错误,并[使用 " 断开 " 关键词退出循环 (https://andsky.com/tech/tutorials/using-break-and-continue-statements-when-working-with-loops-in-go)。 有了这些 " 断点 " ,你不需要包含一个停止循环的条件.
在本节中,我们了解如何声明 ForClause 循环,并使用它通过已知值范围进行迭代,我们还了解如何使用 Condition 循环进行迭代,直到满足特定条件。
通过 RangeClause 顺序数据类型循环
通常在 Go 中使用 for
环节来重复序列或收集数据类型的元素,如 slices, arrayes和 strings. 为了更容易做到这一点,我们可以使用 `for' 环节与 RangeClause 语法。 虽然您可以通过使用 ForClause 语法进行序列数据类型,但 RangeClause 更清洁,更易于阅读。
在我们使用 RangeClause 之前,让我们看看我们如何通过使用 ForClause 语法重复切片:
1[label main.go]
2package main
3
4import "fmt"
5
6func main() {
7 sharks := []string{"hammerhead", "great white", "dogfish", "frilled", "bullhead", "requiem"}
8
9 for i := 0; i < len(sharks); i++ {
10 fmt.Println(sharks[i])
11 }
12}
运行此操作将产生以下输出,打印切片的每个元素:
1[secondary_label Output]
2hammerhead
3great white
4dogfish
5frilled
6bullhead
7requiem
现在,让我们使用RangeClause来执行相同的操作集:
1[label main.go]
2package main
3
4import "fmt"
5
6func main() {
7 sharks := []string{"hammerhead", "great white", "dogfish", "frilled", "bullhead", "requiem"}
8
9 for i, shark := range sharks {
10 fmt.Println(i, shark)
11 }
12}
虽然我们使用了变量),我们会得到相同的输出:
1[secondary_label Output]
20 hammerhead
31 great white
42 dogfish
53 frilled
64 bullhead
75 requiem
当在片段上使用范围
时,它总是会返回两个值. 第一个值将是循环当前迭代的索引,第二个值则是该索引的值. 在这种情况下,对于第一个迭代,索引为0
,并且值是hammerhead
。
有时,我们只想要切片元素中的值,而不是索引. 如果我们更改前面的代码以仅打印值,但是,我们会收到编译时间错误:
1[label main.go]
2package main
3
4import "fmt"
5
6func main() {
7 sharks := []string{"hammerhead", "great white", "dogfish", "frilled", "bullhead", "requiem"}
8
9 for i, shark := range sharks {
10 fmt.Println(shark)
11 }
12}
1[secondary_label Output]
2src/range-error.go:8:6: i declared and not used
由于i
在for
循环中被声明,但从未使用过,编译器将以i declared and not used
的错误响应。
由于这个原因,Go 具有 blank identifier 这是一个 underscore (_
)。 在一个 for
循环中,您可以使用空的标识符忽略从 range
关键字中返回的任何值。在这种情况下,我们希望忽略索引,这是返回的第一个参数。
1[label main.go]
2package main
3
4import "fmt"
5
6func main() {
7 sharks := []string{"hammerhead", "great white", "dogfish", "frilled", "bullhead", "requiem"}
8
9 for _, shark := range sharks {
10 fmt.Println(shark)
11 }
12}
1[secondary_label Output]
2hammerhead
3great white
4dogfish
5frilled
6bullhead
7requiem
此输出显示for
循环通过字符串切片重复,并在没有索引的情况下从字符串切片中打印了每个项目。
您也可以使用范围
将项目添加到列表中:
1[label main.go]
2package main
3
4import "fmt"
5
6func main() {
7 sharks := []string{"hammerhead", "great white", "dogfish", "frilled", "bullhead", "requiem"}
8
9 for range sharks {
10 sharks = append(sharks, "shark")
11 }
12
13 fmt.Printf("%q\n", sharks)
14}
1[secondary_label Output]
2['hammerhead', 'great white', 'dogfish', 'frilled', 'bullhead', 'requiem', 'shark', 'shark', 'shark', 'shark', 'shark', 'shark']
在这里,我们为鲨鱼
片段的长度的每个项目添加了鲨鱼
字符串。
请注意,我们不必使用空标识符 _’ 来忽略从
范围运算器的任何返回值,如果我们不需要使用任何返回值,Go 允许我们将
范围`陈述的整个声明部分放弃。
我们还可以使用范围
运算器来填写片段的值:
1[label main.go]
2package main
3
4import "fmt"
5
6func main() {
7 integers := make([]int, 10)
8 fmt.Println(integers)
9
10 for i := range integers {
11 integers[i] = i
12 }
13
14 fmt.Println(integers)
15}
在本示例中,片段整数
被初始化为十个空值,但for
循环将列表中的所有值设置为:
1[secondary_label Output]
2[0 0 0 0 0 0 0 0 0 0]
3[0 1 2 3 4 5 6 7 8 9]
第一次打印片段整数
的值时,我们看到所有零,然后我们通过每个索引重复并将值设置为当前索引,然后当我们第二次打印整数
的值时,显示它们现在都具有0
到9
的值。
我们还可以使用范围
运算器来迭代字符串中的每个字符串:
1[label main.go]
2package main
3
4import "fmt"
5
6func main() {
7 sammy := "Sammy"
8
9 for _, letter := range sammy {
10 fmt.Printf("%c\n", letter)
11 }
12}
1[secondary_label Output]
2S
3a
4m
5m
6y
当通过 地图重复时,‘范围’将返回 键 和** 值** :
1[label main.go]
2package main
3
4import "fmt"
5
6func main() {
7 sammyShark := map[string]string{"name": "Sammy", "animal": "shark", "color": "blue", "location": "ocean"}
8
9 for key, value := range sammyShark {
10 fmt.Println(key + ": " + value)
11 }
12}
1[secondary_label Output]
2color: blue
3location: ocean
4name: Sammy
5animal: shark
<$>[注] 注: 重要的是要注意,地图返回的顺序是随机的。
现在我们已经学会了如何重复范围``为
循环的序列数据,让我们看看如何在循环中使用循环。
为了路径
循环可以在Go中嵌入,就像其他编程语言一样。 Nesting 是当我们在另一个循环中有一个构造时。 在这种情况下,嵌入循环是发生在另一个循环中的循环。
Nested loops are structurally similar to nested if
statements. They are constructed like so:
1for {
2 [Action]
3 for {
4 [Action]
5 }
6}
该程序首先会遇到外环,执行其第一个迭代。这个第一个迭代会触发内环,然后运行到完成,然后该程序会返回外环的顶部,完成第二次迭代并再次触发内环。
在这个例子中,外部循环将通过一个名为numList
的整数切换,而内部循环将通过一个名为alphaList
的字符串切换。
1[label main.go]
2package main
3
4import "fmt"
5
6func main() {
7 numList := []int{1, 2, 3}
8 alphaList := []string{"a", "b", "c"}
9
10 for _, i := range numList {
11 fmt.Println(i)
12 for _, letter := range alphaList {
13 fmt.Println(letter)
14 }
15 }
16}
当我们运行这个程序时,我们将收到以下输出:
1[secondary_label Output]
21
3a
4b
5c
62
7a
8b
9c
103
11a
12b
13c
输出显示,该程序通过打印1
来完成外环的第一次迭代,然后触发内环的完成,连续打印a
,b
,c
。
嵌入的for
循环可用于迭代由切片组成的切片中的项目,如果我们只使用一个for
循环,该程序将输出每个内部列表作为一个项目:
1[label main.go]
2package main
3
4import "fmt"
5
6func main() {
7 ints := [][]int{
8 []int{0, 1, 2},
9 []int{-1, -2, -3},
10 []int{9, 8, 7},
11 }
12
13 for _, i := range ints {
14 fmt.Println(i)
15 }
16}
1[secondary_label Output]
2[0 1 2]
3[-1 -2 -3]
4[9 8 7]
为了访问内部切片的每个个别项目,我们将实现嵌入的for
循环:
1[label main.go]
2package main
3
4import "fmt"
5
6func main() {
7 ints := [][]int{
8 []int{0, 1, 2},
9 []int{-1, -2, -3},
10 []int{9, 8, 7},
11 }
12
13 for _, i := range ints {
14 for _, j := range i {
15 fmt.Println(j)
16 }
17 }
18}
1[secondary_label Output]
20
31
42
5-1
6-2
7-3
89
98
107
当我们在这里使用嵌入的为
循环时,我们可以重复切片中包含的个别项目。
结论
在本教程中,我们了解了如何声明和使用)。