深入探究for...range语句

这篇具有很好参考价值的文章主要介绍了深入探究for...range语句。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. 引言

在Go语言中,我们经常需要对数据集合进行遍历操作。对于数组来说,使用for语句可以很方便地完成遍历。然而,当我们面对其他数据类型,如map、string 和 channel 时,使用普通的for循环无法直接完成遍历。为了更加便捷地遍历这些数据类型,Go语言引入了for...range语句。本文将以数组遍历为起点,逐步介绍for...range语句在不同数据类型中的应用。

2. 问题引入

假设我们有一个整数数组,我们想要遍历数组中的每个元素并对其进行处理。在这种情况下,我们可以使用for语句结合数组的长度来实现遍历,例如:

package main

import "fmt"

func main() {
    numbers := [5]int{1, 2, 3, 4, 5}

    for i := 0; i < len(numbers); i++ {
        fmt.Println(numbers[i])
    }
}

在上述代码中,我们定义了一个整数数组numbers,通过普通的for循环遍历了数组并打印了每个元素。然而,当我们遇到其他数据类型时,如mapstring 或者channel时,此时使用for语句将无法简单对其进行遍历。那有什么方式能够方便完成对mapstring等类型的遍历呢?

事实上,go语言中存在for....range语句,能够实现对这些类型的遍历,下面我们来仔细介绍下for...range

3. 基本介绍

在Go语言中,for...range语句为遍历数组、切片、映射和通道等数据结构提供了一种便捷的方式。它隐藏了底层的索引或迭代器等细节,是Go语言为遍历各种数据结构提供的一种优雅而简洁的语法糖,使得遍历操作更加方便和直观。下面仔细简介使用for...range完成对切片, map, channel的遍历操作。

3.1 遍历切片

当使用for...range语句遍历切片时,它会逐个迭代切片中的元素,并将索引和对应的值赋值给指定的变量。示例代码如下:

numbers := [5]int{1, 2, 3, 4, 5}

for index, value := range numbers {
    // 在这里处理 index 和 value
}

其中numbers 是我们要遍历的切片。index 是一个变量,它在每次迭代中都会被赋值为当前元素的索引(从0开始)。value 是一个变量,它在每次迭代中都会被赋值为当前元素的值。

如果只关注切片中的值而不需要索引,可以使用下划线 _ 替代索引变量名,以忽略它:

numbers := []int{1, 2, 3, 4, 5}

for _, value := range numbers {
    fmt.Println("Value:", value)
}

这样,循环体只会打印出切片中的值而不显示索引。

通过for...range语句遍历切片,我们可以简洁而直观地访问切片中的每个元素,无需手动管理索引,使得代码更加简洁和易读。

3.2 遍历map

当使用for...range语句遍历map时,它会迭代映射中的每个键值对,并将键和对应的值赋值给指定的变量。示例代码如下:

students := map[string]int{
    "Alice":   25,
    "Bob":     27,
    "Charlie": 23,
}

for key, value := range students {
    // 在这里处理 key 和 value
}

这里for...range会遍历所有的键值对,无需我们去手动处理迭代器的逻辑,即可完成对map的遍历操作。

3.3 遍历string

当使用for...range语句遍历字符串时,它会逐个迭代字符串中的字符,并将每个字符的索引和值赋值给指定的变量。以下是遍历字符串的示例代码:

text := "Hello, 世界!"

for index, character := range text {
    fmt.Printf("Index: %d, Character: %c\n", index, character)
}

输出结果为:

Index: 0, Character: H
Index: 1, Character: e
Index: 2, Character: l
Index: 3, Character: l
Index: 4, Character: o
Index: 5, Character: ,
Index: 6, Character:  
Index: 7, Character: 世
Index: 10, Character: 界

需要注意的是,Go语言中的字符串是以UTF-8编码存储的,UTF-8是一种变长编码,不同的Unicode字符可能会占用不同数量的字节。而index的值表示每个字符在字符串中的字节索引位置,所以字符的索引位置并不一定是连续的。

这里通过for...range语句遍历字符串,我们可以方便地处理每个字符,无需手动管理索引和字符编码问题,使得处理字符串的逻辑更加简洁和易读。

3.4 遍历channel

当使用for...range语句遍历channel时,它会迭代通道中的每个值,直到通道关闭为止。下面是一个示例代码:

ch := make(chan int)

// 向通道写入数据的例子
go func() {
    ch <- 1
    ch <- 2
    ch <- 3
    close(ch)
}()

// 将输出 1 2 3
for value := range ch {
    fmt.Println("Value:", value)
}

在示例中,我们向通道写入了3个整数值。然后,使用for...range语句遍历通道,从中获取每个值并进行处理。

需要注意的是,如果通道中没有数据可用,for...range语句会阻塞,直到有数据可用或通道被关闭。因此,当通道中没有数据时,它会等待数据的到达。

通过for...range语句遍历通道,可以非常方便得不断从channel中取出数据,然后对其进行处理。

4. 注意事项

for...range语句可以认为是go语言的一个语法糖,简化了我们对不同数据结构的遍历操作,但是使用for...range语句还是存在一些注意事项的,充分了解这些注意事项,能够让我们更好得使用该特性,下面我们将对其来进行叙述。

4.1 迭代变量是会被复用的

当使用for...range循环时,迭代变量是会被复用的。这意味着在每次循环迭代中,迭代变量都将被重用,而不是在每次迭代中创建一个新的迭代变量。

下面是一个简单的示例代码,演示了迭代变量被复用的情况:

package main

import "fmt"

func main() {
        numbers := []int{1, 2, 3, 4, 5}

        for _, value := range numbers {
           go func() {
              fmt.Print(strconv.Itoa(value) + " ")
           }()
        }
}

在上述代码中,我们使用for...range循环遍历切片numbers,并在每次循环迭代中创建一个匿名函数并启动一个goroutine。该匿名函数打印当前迭代的value变量。下面是一个可能的结果:

4 5 5 5 5

出现这个结果的原因,就是由于迭代变量被复用,所有的goroutine都会共享相同的value变量。当goroutine开始执行时,它们可能会读取到最后一次迭代的结果,而不是预期的迭代顺序。这会导致输出结果可能是重复的数字或者不按照预期的顺序输出。

如果不清楚迭代变量会被复用的特点,这个在某些场景下可能会导致意料之外结果的出现。因此,如果for...range循环中存在并发操作,延迟函数等操作时,同时也依赖于迭代变量的值,这个时候需要确保在循环迭代中创建新的副本,以避免意外的结果。

4.2 参与迭代的为range表达式的副本数据

对于for...range循环,是使用range表达式的副本数据进行迭代。这意味着迭代过程中对原始数据的修改,并不会对迭代的结果造成影响,一个简单的代码示例如下:

package main

import "fmt"

func main() {
        numbers := [5]int{1, 2, 3, 4, 5}
        for i, v := range numbers {
           if i == 0 {
              numbers[1] = 100 // 修改原始数据的值
              numbers[2] = 200
           }
           fmt.Println("Index:", i, "Value:", v)
        }
}

在上述代码中,我们使用for...range循环遍历数组numbers, 然后在循环体内修改了数组中元素的值。遍历结果如下:

Index: 0 Value: 1
Index: 1 Value: 2
Index: 2 Value: 3
Index: 3 Value: 4
Index: 4 Value: 5

可以看到,虽然在迭代过程中,对numbers进行遍历,但是并没有影响到遍历的结果。从这里也可以证明,参与迭代的为range表达式的副本数据,而不是副本数据。

如果循环中的操作,需要依赖中间修改后的数据结果,此时最好分成两个遍历,首先遍历数据,修改其中的数据,之后再遍历修改后的数据。对上述代码改进如下:

numbers := [5]int{1, 2, 3, 4, 5}
// 1. 第一个遍历修改数据
for i, _ := range numbers {
   if i == 0 {
      numbers[1] = 100 // 修改原始数据的值
      numbers[2] = 200
   }

}
// 2. 第二个遍历输出数据
for i, v := range numbers {
   fmt.Println("Index:", i, "Value:", v)
}

这次遍历的结果,就是修改后的数据,如下:

Index: 0 Value: 1
Index: 1 Value: 100
Index: 2 Value: 200
Index: 3 Value: 4
Index: 4 Value: 5

4.3 map遍历顺序是不确定的

对于Go语言中的map类型,遍历其键值对时的顺序是不确定的,下面是一个简单代码的示例:

package main

import "fmt"

func main() {
        data := map[string]int{
                "apple":  1,
                "banana": 2,
                "cherry": 3,
        }

        for key, value := range data {
                fmt.Println(key, value)
        }
}

运行上述代码,每次输出的结果可能是不同的,即键值对的顺序是不确定的。有可能第一次运行的结果为:

banana 2
cherry 3
apple 1

然后第二次运行的结果又与第一次运行的结果不同,可能为:

apple 1
banana 2
cherry 3

从这个例子可以证明,对map进行遍历,其遍历顺序是不固定的,所以我们需要注意,不能依赖map的遍历顺序。

如果需要每次map中的数据按照某个顺序输出,此时可以先把key保存到切片中,对切片按照指定的顺序进行排序,之后遍历排序后的切片,并使用切片中的key来访问map中的value。此时map中的数据便能够按照指定的顺序来输出,下面是一个简单的代码代码示例:

package main

import (
        "fmt"
        "sort"
)

func main() {
        data := map[string]int{
                "apple":  1,
                "banana": 2,
                "cherry": 3,
        }

        // 创建保存键的切片
        keys := make([]string, 0, len(data))
        for key := range data {
                keys = append(keys, key)
        }

        // 对切片进行排序
        sort.Strings(keys)

        // 按照排序后的键遍历map
        for _, key := range keys {
                value := data[key]
                fmt.Println(key, value)
        }
}

5. 总结

本文对Go语言中的for...range进行了基本介绍,首先从一个简单遍历问题出发,发现基本的for语句似乎无法简单实现对stringmap等类型的遍历操作,从而引出了for...range语句。

接着我们仔细介绍了,如何使用for...rangestring,map,channel等类型的遍历操作。然后我们再仔细介绍了使用for...range的三个注意事项,如参与迭代的为range表达式的副本数据。通过对这些注意事项的了解,我们能够更好得使用for...range语句,避免出现预料之外的情况。

基于以上内容,完成了对for...range的介绍,希望能帮助你更好地理解和使用这个重要的Go语言特性。文章来源地址https://www.toymoban.com/news/detail-480754.html

到了这里,关于深入探究for...range语句的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • AI伦理问题探究:人工智能对我们的未来会产生什么样的影响?

    “人工智能(AI)已经成为当今世界的热点话题。它正在影响着我们的生活,改变着我们的世界。但是,AI的发展也带来了一系列的伦理问题和社会影响。本文将详细介绍AI的伦理问题和社会影响,并探讨这些问题可能对我们未来的生活产生的影响。” 1. 隐私问题😱 AI的发展

    2024年02月04日
    浏览(43)
  • python生成矩阵为何[[0 for i in range(n)] for j in range(m)]而不能[[0]*n]*m

    python生成矩阵,使用[[0]*n]*m,我们会发现,当改变其中某一个元素时,整列数据都会发生改变,而使用[[0 for i in range(n)] for j in range(m)]才可以生成正常的矩阵。 这是因为,list是可变元素,而int是不可变元素,对于list存储采用指针,引用型变量,改变矩阵其中某一个元素值,

    2023年04月17日
    浏览(28)
  • 断点续传下载:深入理解 HTTP Header 中的 Range 和 Content-Range 参数

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bAhEY9hc-1687309020087)(https://example.com/resume-download-image)] 在进行文件下载时,我们经常会遇到网络不稳定或其他中断情况。为了提供更好的用户体验和节省带宽资源,断点续传技术应运而生。HTTP 协议通过

    2024年02月10日
    浏览(45)
  • go中for range的坑以及解决方案

    相信小伙伴都遇到过以下的循环变量的问题,那是因为循环的val变量是重复使用的,即仅有一份。也就是说,每次循环后赋给val的值就会把前面循环赋给val的值替换掉,所以打印出来的值都是最后一次循环赋给val的值。 使用局部变量/临时变量,即可解决         可以设置

    2024年01月25日
    浏览(29)
  • 深入浅出MySQL MRR(Multi-Range Read)

    本文已收录至GitHub,推荐阅读 👉 Java随想录 微信公众号:Java随想录 原创不易,注重版权。转载请注明原作者和原文链接 目录 什么是MRR MRR如何使用 在探索数据库优化的广阔领域中,我们不可避免地会遇到一系列独特的概念和技术。其中之一就是MySQL的多范围读取(Multi-Rang

    2024年02月08日
    浏览(27)
  • 论文阅读:Rethinking Range View Representation for LiDAR Segmentation

    来源ICCV2023 LiDAR分割对于自动驾驶感知至关重要。最近的趋势有利于基于点或体素的方法,因为它们通常产生比传统的距离视图表示更好的性能。在这项工作中,我们揭示了建立强大的距离视图模型的几个关键因素。我们观察到, “多对一”的映射 , 语义不连贯性 , 形状变

    2024年02月02日
    浏览(37)
  • 工作中,我们经常用到哪些SQL语句呢?

    目录 一、DDL部分(create、drop、alter) 1.1 create 语句上 1.2 drop 语句 1.3 alter 语句 二、DML(数据操纵语言)和DQL(数据查询语言) 2.1 insert 语句 2.2 update 语句 2.3 delete 语句 2.4 select 语句 2.5 其他操纵语言 2.5.1 truncate 语句 2.5.2 merge 语句 三、用户角色权限 3.1 用户相关 3.1.1 创建用户

    2024年02月03日
    浏览(33)
  • 为什么 Go for-range 的 value 值地址每次都一样?

    原文链接: 为什么 Go for-range 的 value 值地址每次都一样? 循环语句是一种常用的控制结构,在 Go 语言中,除了 for 以外,还有一个 range ,可以使用 for-range 循环迭代数组、切片、字符串、map 和 channel 这些数据类型。 但是在使用 for-range 循环迭代数组和切片的时

    2024年02月01日
    浏览(34)
  • 深入浅出:探究过完备字典矩阵

    在数学和信号处理的世界里,我们总是在寻找表达数据的最佳方式。在这篇博文中,我们将探讨一种特殊的矩阵——过完备字典矩阵,这是线性代数和信号处理中一个非常有趣且实用的概念。 首先,我们先来理解一下字典矩阵的概念。在数学上,字典矩阵基本上就是一组向量

    2024年03月17日
    浏览(26)
  • 如何理解Go言中的Context?

    目前看过除了《go语言程序设计》以外最好的教程:https://www.practical-go-lessons.com 原文:https://www.practical-go-lessons.com/chap-37-context 你将在本章中学到什么? 1.什么是上下文? 2.什么是链表? 3.如何使用上下文包? 涵盖的技术概念 Context derivation Linked list Context key-value pair Cancella

    2024年02月08日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包