跳转到主要内容

介绍


在计算机编程中,循环是循环以重复执行一段代码的代码结构,通常直到满足某些条件。在计算机编程中使用循环可以让您多次自动化和重复类似的任务。想象一下,如果您有一个需要处理的文件列表,或者您想计算一篇文章的行数。您将在代码中使用循环来解决这些类型的问题。

在 Go 中,for 循环根据循环计数器或循环变量实现代码的重复执行。与其他具有多个循环结构(如 while、do 等)的编程语言不同,Go 只有 for 循环。这有助于使您的代码更清晰和更具可读性,因为您不必担心多种策略来实现相同的循环结构。在开发过程中,这种增强的可读性和减少的认知负担也将使您的代码比其他语言更不容易出错。

在本教程中,您将了解 Go 的 for 循环是如何工作的,包括其使用的三个主要变体。我们将首先展示如何创建不同类型的 for 循环,然后介绍如何在 Go 中遍历顺序数据类型。最后,我们将解释如何使用嵌套循环。

声明子句和条件循环


为了解决各种用例,在 Go 中创建 for 循环有三种不同的方法,每种方法都有自己的功能。这些用于创建带有 Condition、ForClause 或 RangeClause 的 for 循环。在本节中,我们将解释如何声明和使用 ForClause 和 Condition 变体。

让我们先看看如何使用 ForClause 的 for 循环。

ForClause 循环被定义为有一个初始语句,后跟一个条件,然后是一个 post 语句。它们按以下语法排列:

for [ Initial Statement ] ; [ Condition ] ; [ Post Statement ] {
    [Action]
}


为了解释前面的组件的作用,让我们看一个使用 ForClause 语法在指定值范围内递增的 for 循环:

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++ 递增运算符进行迭代时,我们将循环变量 i 递增 1。

当我们运行这个程序时,输出如下所示:

Output
0
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(不包括),所以输出如下所示:

Output
20
21
22
23
24


我们还可以使用我们的 post 语句以不同的值递增。这与其他语言中的 step 类似:

首先,让我们使用具有正值的 post 语句:

for i := 0; i < 15; i += 3 {
    fmt.Println(i)
}


在这种情况下,设置了 for 循环,以便打印从 0 到 15 的数字,但增量为 3,因此仅打印每三个数字,如下所示:

Output
0
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。我们可以在输出中看到这种情况:

Output
100
90
80
70
60
50
40
30
20
10


您还可以从 for 语法中排除初始语句和 post 语句,并且只使用条件。这就是所谓的条件循环:

i := 0
for i < 5 {
    fmt.Println(i)
    i++
}


这一次,我们在前面的代码行中将变量 i 与 for 循环分开声明。循环只有一个条件子句,用于检查 i 是否小于 5。只要条件计算为真,循环就会继续迭代。

有时您可能不知道完成某项任务所需的迭代次数。在这种情况下,您可以省略所有语句,并使用 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 循环。使用这些断点,您无需包含停止循环的条件。

在本节中,我们学习了如何声明 ForClause 循环并使用它来迭代已知范围的值。我们还学习了如何使用条件循环进行迭代,直到满足特定条件。接下来,我们将了解 RangeClause 如何用于迭代顺序数据类型。

使用 Range 子句遍历顺序数据类型


在 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])
    }
}

运行它将给出以下输出,打印出切片的每个元素:

Output
hammerhead
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,但我们可以将变量称为任何其他有效的变量名,我们会得到相同的输出:

Output
0 hammerhead
1 great white
2 dogfish
3 frilled
4 bullhead
5 requiem


在切片上使用范围时,它将始终返回两个值。第一个值将是循环的当前迭代所在的索引,第二个是该索引处的值。在这种情况下,对于第一次迭代,索引为 0,值为锤头。

有时,我们只想要切片元素内的值,而不是索引。但是,如果我们将前面的代码更改为仅打印值,我们将收到编译时错误:

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)
    }
}

Output
src/range-error.go:8:6: i declared and not used


因为在 for 循环中声明了 i,但从未使用过,所以编译器将响应 i 声明但未使用的错误。每当您声明一个变量并且不使用它时,您都会在 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)
    }
}

 

Output
hammerhead
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']


在这里,我们为鲨鱼切片长度的每个项目添加了一个占位符字符串"shark"。

请注意,我们不必使用空白标识符 _ 来忽略范围运算符的任何返回值。如果我们不需要使用任何一个返回值,Go 允许我们省略 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)
}

在这个例子中,切片整数用十个空值初始化,但是 for 循环设置列表中的所有值,如下所示:

Output
[0 0 0 0 0 0 0 0 0 0]
[0 1 2 3 4 5 6 7 8 9]


第一次打印切片整数的值时,我们看到全为零​​。然后我们遍历每个索引并将值设置为当前索引。然后当我们第二次打印整数的值时,显示它们现在都具有 0 到 9 的值。

我们还可以使用范围运算符来遍历字符串中的每个字符:

main.go

package main

import "fmt"

func main() {
    sammy := "Sammy"

    for _, letter := range sammy {
        fmt.Printf("%c\n", letter)
    }
}

 

Output
S
a
m
m
y


遍历地图时,范围将返回键和值:

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)
    }
}

Output
color: blue
location: ocean
name: Sammy
animal: shark


注意:重要的是要注意地图返回的顺序是随机的。每次运行此程序时,您可能会得到不同的结果。

现在我们已经学习了如何使用 range for 循环来迭代顺序数据,让我们看看如何在循环中使用循环。

嵌套 For 循环


循环可以嵌套在 Go 中,就像其他编程语言一样。嵌套是当我们在另一个内部有一个构造时。在这种情况下,嵌套循环是发生在另一个循环中的循环。当您希望对数据集的每个元素执行循环操作时,这些可能很有用。

嵌套循环在结构上类似于嵌套 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)
		}
	}
}

当我们运行这个程序时,我们将收到以下输出:




Output
1
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)
        }
    }
}

Output
0
1
2
-1
-2
-3
9
8
7


当我们在这里使用嵌套的 for 循环时,我们能够迭代切片中包含的各个项目。

结论


在本教程中,我们学习了如何声明和使用 for 循环来解决 Go 中的重复任务。我们还学习了 for 循环的三种不同变体以及何时使用它们。要了解有关 for 循环以及如何控制它们的流程的更多信息,请阅读在 Go 中使用循环时使用 Break 和 Continue 语句。

文章链接