介绍
在计算机编程中, 循环是循环以重复执行一段代码的代码结构,通常直到满足某些条件。 在计算机编程中使用循环允许您多次自动执行和重复类似的任务。 想象一下,如果您有一个需要处理的文件列表,或者您想要计算文章中的行数。 您可以在代码中使用循环来解决这些类型的问题。
在Go中, for
循环实现基于循环计数器或循环变量重复执行代码。 与其他具有多个循环结构的编程语言(如while
, do
等)不同,Go只有for
循环。 这有助于使您的代码更清晰,更易读,因为您不必担心多种策略来实现相同的循环结构。 在开发过程中增强的可读性和减少的认知负荷也将使您的代码比其他语言更容易出错。
在本教程中,您将学习Go的for
循环如何工作,包括其使用的三个主要变体。 我们将首先展示如何创建不同类型的for
循环,然后介绍如何在Go中循环遍历顺序数据类型 。 我们将通过解释如何使用嵌套循环来结束。
声明ForClause和Condition循环
为了解释各种用例,在Go中创建for
循环有三种不同的方法,每种方法都有自己的功能。 这些是使用Condition , ForClause或RangeClause创建for
循环。 在本节中,我们将解释如何声明和使用ForClause和Condition变体。
让我们看一下如何首先使用ForClause的for
循环。
ForClause循环定义为具有初始语句 ,后跟条件 ,然后是post语句 。 这些按以下语法排列:
for [ Initial Statement ] ; [ Condition ] ; [ Post Statement ] { [Action]}
为了解释前面的组件的作用,让我们看看一个for
循环,它使用ForClause语法递增指定的值范围:
for i := 0; i < 5; i++ { fmt.Println(i)}
让我们打破这个循环并识别每个部分。
循环的第一部分是i := 0
。 这是最初的声明:
for i := 0; i < 5; i++ { fmt.Println(i)}
它声明我们声明一个名为i
的变量,并将初始值设置为0
。
接下来是条件:
for i := 0; i < 5; i++ { fmt.Println(i)}
在这种情况下,我们声明当i
小于值5
,循环应该继续循环。
最后,我们有帖子声明:
for i := 0; i < 5; i++ { fmt.Println(i)}
在post语句中,每次使用i++
increment运算符进行迭代时,我们将循环变量i
递增1 。
当我们运行这个程序时,输出如下所示:
01234
循环运行了5次。 最初,它将i
设置为0
,然后检查以查看i
是否小于5
。 由于i
的值小于5
,执行循环并执行fmt.Println(i)
的动作。 循环结束后,调用i++
的post语句, i++
的值增加1。
注意:请记住,在编程中我们倾向于从索引0开始,所以这就是为什么虽然打印出5个数字,但它们的范围是0-4。
我们不限于从0开始或以指定值结束。 我们可以为初始语句分配任何值,也可以在post语句中的任何值处停止。 这允许我们创建任何所需的范围来循环:
for i := 20; i < 25; i++ { fmt.Println(i)}
这里,迭代从20(包括)到25(不包括),因此输出如下所示:
2021222324
我们也可以使用post语句以不同的值递增。 这与其他语言的step
类似:
首先,让我们使用带有正值的post语句:
for i := 0; i < 15; i += 3 { fmt.Println(i)}
在这种情况下,设置for
循环以便打印出0到15之间的数字,但是增量为3,因此只打印每三个数字,如下所示:
036912
我们也可以使用post语句参数的负值来向后迭代,但是我们必须相应地调整我们的初始语句和条件参数:
for i := 100; i > 0; i -= 10 { fmt.Println(i)}
这里,我们将i
设置为初始值100
,使用条件i < 0
停止为0
,post语句使用-=
运算符将值减小10。 循环从100
开始并在0
结束,每次迭代减少10。 我们可以在输出中看到这种情况:
100908070605040302010
您还可以从for
语法中排除初始语句和post语句,并仅使用条件。 这就是所谓的Condition循环 :
i := 0for i < 5 { fmt.Println(i) i++}
这一次,我们将变量i
与前一行代码中的for
循环分开声明。 该循环只有一个条件子句,用于检查i
是否小于5
。 只要条件的计算结果为true
,循环就会继续迭代。
有时您可能不知道完成某项任务所需的迭代次数。 在这种情况下,您可以省略所有语句,并使用break
关键字退出执行:
for { if someCondition { break } // do action here}
这方面的一个例子可能是,如果我们从一个不确定大小的结构中读取缓冲区 ,我们不知道什么时候会完成阅读:
package mainimport ( "bytes" "fmt" "io")func main() { buf := bytes.NewBufferString("one\ntwo\nthree\nfour\n") for { line, err := buf.ReadString('\n') if err != nil { if err == io.EOF { fmt.Print(line) break } fmt.Println(err) break } fmt.Print(line) }}
在前面的代码中, buf :=bytes.NewBufferString("one\ntwo\nthree\nfour\n")
声明了一个带有一些数据的缓冲区。 因为我们不知道缓冲区何时完成读取,所以我们创建一个没有子句的for
循环。 在for
循环中,我们使用line, err := buf.ReadString('\n')
从缓冲区中读取一行,并检查是否从缓冲区读取错误。 如果有,我们解决错误,并使用break
关键字退出for循环 。 使用这些break
,您不需要包含停止循环的条件。
在本节中,我们学习了如何声明ForClause循环并使用它来迭代已知的值范围。 我们还学习了如何使用Condition循环进行迭代,直到满足特定条件。 接下来,我们将学习如何使用RangeClause迭代顺序数据类型。
使用RangeClause循环使用顺序数据类型
在Go中使用for
循环来迭代遍历切片,数组和字符串等顺序或集合数据类型的元素。 为了更容易实现,我们可以使用带有RangeClause语法的for
循环。 虽然您可以使用ForClause语法循环遍历顺序数据类型,但RangeClause更清晰,更易于阅读。
在我们看一下使用RangeClause之前,让我们看看如何使用ForClause语法迭代切片:
package mainimport "fmt"func main() { sharks := []string{"hammerhead", "great white", "dogfish", "frilled", "bullhead", "requiem"} for i := 0; i < len(sharks); i++ { fmt.Println(sharks[i]) }}
运行此命令将提供以下输出,打印出切片的每个元素:
hammerheadgreat whitedogfishfrilledbullheadrequiem
现在,让我们使用RangeClause来执行同一组操作:
package mainimport "fmt"func main() { sharks := []string{"hammerhead", "great white", "dogfish", "frilled", "bullhead", "requiem"} for i, shark := range sharks { fmt.Println(i, shark) }}
在这种情况下,我们打印出列表中的每个项目。 虽然我们使用了变量i
和shark
,但我们可以调用变量任何其他有效的变量名 ,我们会得到相同的输出:
0 hammerhead1 great white2 dogfish3 frilled4 bullhead5 requiem
在切片上使用range
时,它将始终返回两个值。 第一个值是循环的当前迭代所在的索引,第二个值是该索引处的值。 在这种情况下,对于第一次迭代,索引为0
,值为hammerhead
。
有时,我们只想要切片元素中的值,而不是索引。 如果我们将前面的代码更改为仅打印出值,我们将收到编译时错误:
package mainimport "fmt"func main() { sharks := []string{"hammerhead", "great white", "dogfish", "frilled", "bullhead", "requiem"} for i, shark := range sharks { fmt.Println(shark) }}
src/range-error.go:8:6: i declared and not used
因为i
在for
循环中声明,但从未使用过,编译器将响应i declared and not used
的错误i declared and not used
。 这是您在Go声明变量并且不使用它时将在Go中收到的相同错误。
因此,Go具有空心标识符 ,它是下划线( _
)。 在for
循环中,您可以使用空白标识符来忽略range
关键字返回的任何值。 在这种情况下,我们要忽略索引,这是返回的第一个参数。
package mainimport "fmt"func main() { sharks := []string{"hammerhead", "great white", "dogfish", "frilled", "bullhead", "requiem"} for _, shark := range sharks { fmt.Println(shark) }}
hammerheadgreat whitedogfishfrilledbullheadrequiem
此输出显示for
循环遍历字符串切片,并从没有索引的切片打印每个项目。
您还可以使用range
将项添加到列表中:
package mainimport "fmt"func main() { sharks := []string{"hammerhead", "great white", "dogfish", "frilled", "bullhead", "requiem"} for range sharks { sharks = append(sharks, "shark") } fmt.Printf("%q\n", sharks)}
['hammerhead', 'great white', 'dogfish', 'frilled', 'bullhead', 'requiem', 'shark', 'shark', 'shark', 'shark', 'shark', 'shark']
在这里,我们为sharks
切片长度的每个项目添加了一个占位符字符串"shark"
。
请注意,我们不必使用空标识符_
来忽略range
运算符的任何返回值。 如果我们不需要使用任何一个返回值,Go允许我们省略range
语句的整个声明部分。
我们还可以使用range
运算符来填充切片的值:
package mainimport "fmt"func main() { integers := make([]int, 10) fmt.Println(integers) for i := range integers { integers[i] = i } fmt.Println(integers)}
在此示例中,切片integers
初始化为十个空值,但for
循环设置列表中的所有值,如下所示:
[0 0 0 0 0 0 0 0 0 0][0 1 2 3 4 5 6 7 8 9]
我们第一次打印切片integers
的值时,我们看到全部为零。 然后我们遍历每个索引并将值设置为当前索引。 然后当我们第二次打印整数值时,显示它们现在都具有0
到9
的值。
我们还可以使用range
运算符迭代字符串中的每个字符:
package mainimport "fmt"func main() { sammy := "Sammy" for _, letter := range sammy { fmt.Printf("%c\n", letter) }}
Sammy
迭代地图时 , range
将返回键和值 :
package mainimport "fmt"func main() { sammyShark := map[string]string{"name": "Sammy", "animal": "shark", "color": "blue", "location": "ocean"} for key, value := range sammyShark { fmt.Println(key + ": " + value) }}
color: bluelocation: oceanname: Sammyanimal: shark
注意:请务必注意,地图返回的顺序是随机的。 每次运行此程序时,您可能会得到不同的结果。
现在我们已经学会了如何使用range
for
循环遍历顺序数据,让我们看一下如何在循环内部使用循环。
嵌套循环
循环可以嵌套在Go中,因为它们可以与其他编程语言嵌套。 嵌套是指我们在另一个内部有一个构造。 在这种情况下,嵌套循环是在另一个循环中发生的循环。 当您希望对数据集的每个元素执行循环操作时,这些操作非常有用。
嵌套循环在结构上类似于嵌套的if
语句 。 它们的构造如下:
for { [Action] for { [Action] }}
程序首先遇到外循环,执行第一次迭代。 第一次迭代触发内部嵌套循环,然后运行完成。 然后程序返回到外部循环的顶部,完成第二次迭代并再次触发嵌套循环。 同样,嵌套循环运行完成,程序返回到外循环的顶部,直到序列完成或中断或其他语句中断进程。
让我们实现一个嵌套的for
循环,以便我们仔细看看。 在这个例子中,外部循环将迭代一个名为numList
的整数切片,内部循环将遍历一个名为alphaList
的字符串切片。
package mainimport "fmt"func main() { numList := []int{1, 2, 3} alphaList := []string{"a", "b", "c"} for _, i := range numList { fmt.Println(i) for _, letter := range alphaList { fmt.Println(letter) } }}
当我们运行此程序时,我们将收到以下输出:
1abc2abc3abc
输出说明程序通过打印1
完成外循环的第一次迭代,然后触发内循环的完成,连续打印a
, b
, c
。 一旦内循环完成,程序返回到外循环的顶部,打印2
,然后再次打印整个内循环( a
, b
, c
)等。
嵌套for
循环可用于迭代由切片组成的切片内的项目。 在由切片组成的切片中,如果我们只使用一个for
循环,程序将输出每个内部列表作为项:
package mainimport "fmt"func main() { ints := [][]int{ []int{0, 1, 2}, []int{-1, -2, -3}, []int{9, 8, 7}, } for _, i := range ints { fmt.Println(i) }}
[0 1 2][-1 -2 -3][9 8 7]
为了访问内部切片的每个单独项,我们将实现嵌套的for
循环:
package mainimport "fmt"func main() { ints := [][]int{ []int{0, 1, 2}, []int{-1, -2, -3}, []int{9, 8, 7}, } for _, i := range ints { for _, j := range i { fmt.Println(j) } }}
012-1-2-3987
当我们在这里使用嵌套for
循环时,我们能够遍历切片中包含的各个项目。
结论
在本教程中,我们学习了如何声明和使用for
循环来解决Go中的重复任务。 我们还学习了for
循环的三种不同变体以及何时使用它们。 要了解有关for
循环以及如何控制它们的流程的更多信息,请阅读在Go中使用循环时使用中断和继续语句 。