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