上一讲我们介绍了Go语言中的容器,即数组、切片和集合变量,并掌握了它们各自的声明和赋值技巧。我们都知道,为某个变量赋值,最终是为了使用它。那么,这一节我们会继续深入,介绍如下内容:

  1. 获取数组、切片和集合的元素个数
  2. 获取和修改数组、切片和集合的元素的值
  3. 数组、切片和集合的循环遍历

# 获取元素个数

相信朋友们还记得那个查找素数的例子,在上一讲中我们实现了查找范围是10以内的素数。现在要求变了,需要查找1000以内素数的个数。

为了方便阅读,我把上一讲中的代码示例搬了过来:

go
复制代码func main() {
   var resultSlice []int
   for i := 2; i < 10; i++ {
      //假定i为素数
      flag := true
      for j := 2; j < i; j++ {
         if i%j == 0 {
            //当i能被某个整数整除时,不是素数
            flag = false
         }
      }
      //如果依旧为true,则i为素数
      if flag {
         //将素数存放到resultArray数组中
         resultSlice = append(resultSlice, i)
      }
   }
   fmt.Println(resultSlice)
}

本例使用了切片而非数组或集合,为什么这样做呢?

因为我们不清楚容器内到底有多少个元素,因此数组不合适。查找到的结果是若干正整数,无需“键-值对”这样的数据结构,因此集合也不合适。

若要改变查找范围为1000以内,只需要修改最外层for循环的终止条件:将i < 10改为i < 1000即可。但运行后发现,输出实在是太多了。10以内的素数仅有4个,看一眼便可得出结论。但1000以内的话,简直无法数得清。

幸好Go语言内置了获取容器内元素个数的函数,这个函数对数组、切片和集合都有效,且用法相同,调用格式为:

go
复制代码len(variable)

其中,len()表示调用函数,是固定写法;variable被称为“参数”,表示向函数中“传递”的变量;调用后,函数将返回元素个数,返回值的类型是int。

💡 提示: 有关函数的更多内容,将在下一讲中详述,这里仅需做到了解、会用即可。

对于本例而言,获取素数个数只需编写如下代码即可实现:

go
复制代码fmt.Println(len(resultSlice))

下面,将这句代码添加到原有代码中,再次运行程序即可看到控制台上输出元素个数了。

在实际工作中,对于函数的调用是很常见的做法。通过本例能体会到,作为函数的调用者,无需关注函数内部是如何实现的(如本例中统计容器内元素个数的具体实现)。这一机制极大降低了重复功能的代码量,还能在一定程度上降低Bug的数量。

但劣势也很明显,当我们不清楚函数在做什么时,就没有信心调用它了。此时,可以通过函数名(如本例的len()函数,对应英文单词的length,表示容器的长度,也就是容器内元素的个数)、阅读函数注释等方法来了解(回忆一下曾经讲过的“代码规范”,在第5讲 附录二 (opens new window))。

此外,对于陌生的函数,还可以在已知结果的前提下进行初步验证。对于本例而言,就可以先保持“查找10以内的素数”这一条件不变。然后调用len()函数获取resultSlice切片变量的元素个数,看看是不是4。如此,便可初步验证len()函数的作用和正确性了。

当然,这样的验证做得越多,结果越可信。这种“验证”的思路还可用于代码测试,鉴于小册的内容结构,这部分暂不展开详述,在后续的章节中再做介绍。

# 获取/修改元素的值

有些时候,我们还需要获取和/或修改某个特定元素的值。我们还是用查找素数作为例子,这次的要求是逐个输出10以内整数是否为素数。结果如下:

0 false 1 false 2 true 3 true 4 false 5 true 6 false 7 true 8 false 9 false 10 false

可以看到,第3、4、6、8个元素(索引值为2、3、5、7)的值为true,它们都是素数,其它元素值为false,不是素数。请大家想想看,如何实现呢?

为了讲解元素值的修改方法,在此给各位提供一个实现思路,一共分为三个步骤:

  1. 声明元素个数为11,值类型为bool的数组。并附所有元素初值为false,假定所有元素皆非素数;
  2. 判断2-10范围内的素数,若是素数,将数组相应索引表示的值改为true;
  3. 输出数组元素的索引和索引表示的值。

接下来实现上面三个步骤。

声明并赋初值是上一讲的内容,使用for循环实现:

go
复制代码// 声明元素类型为bool的数组。索引表示正整数;值表示是否为素数,true为是。
var resultArray [11]bool
// 赋初值,皆为false
for i := 0; i < 11; i++ {
   resultArray[i] = false
}

接着,修改查找素数的逻辑如下:

go
复制代码// 素数判定
for i := 2; i < 10; i++ {
   //假定i为素数
   flag := true
   for j := 2; j < i; j++ {
      if i%j == 0 {
         //当i能被某个整数整除时,不是素数
         flag = false
      }
   }
   //如果依旧为true,则i为素数
   if flag {
      resultArray[i] = true
   }
}

由于0和1不是素数,因此无需理会,循环依旧从2开始。当查找到素数时,将resultArray数组中相应索引所表示的值改为true。如:2是素数,resultArray[2]的值将被改为true。

最后,使用循环依次输出resultArray的索引和索引表示的值:

go
复制代码for i := 0; i < 11; i++ {
   fmt.Println(i, resultArray[i])
}

将上述三个步骤依次结合起来,并运行程序,最终将得到所要求的输出结果。

在实现的过程中不难发现,访问数组中元素时,代码为:resultArray[i];修改数组中元素的值实际和赋值相同。

由此我们归纳出如下规律:

  • 修改数组、切片和集合中的值,方法与赋值相同
  • 获取数组或切片的值,格式为variable[index]。variable表示数组或切片的变量名;index表示索引值。
  • 获取集合的值,格式为variable[key_value]。variable表示集合的变量名;key_value表示键的值。

# 循环遍历

在上一个例子中,我们使用了for循环实现了resultArray数组的赋值和输出。

对于数组和切片而言,使用for循环是实现遍历的途径之一,但往往无法适用于集合。

对于数组和切片而言,索引是从0开始的整数,可以使用索引作为循环条件。但集合的的取值方式是通过“键”,而键的值就不一定是数字了。非数字的键,往往会是字符串。

❗️ 注意: 数组和切片是有序存储的,因此可用索引来循环;集合是无序的,在使用时要格外小心。

我们还是回顾上一讲中使用过的集合示例:

go
复制代码func main() {
   var studentInfos = make(map[string]string)
   studentInfos["0001"] = "王小红"
   studentInfos["0002"] = "李小明"
   studentInfos["0003"] = "张三丰"
   studentInfos["0004"] = "孙小贝"
   studentInfos["0005"] = "何明明"
   // 输出语句
   fmt.Println(studentInfos)
}

运行后,控制台会一口气依次输出所有studentInfos集合的键值对,如下所示:

map[0001:王小红 0002:李小明 0003:张三丰 0004:孙小贝 0005:何明明]

现在,需要输出更易于使用者理解的格式,具体如下:

学号: 0004 姓名: 孙小贝 学号: 0005 姓名: 何明明 学号: 0001 姓名: 王小红 学号: 0002 姓名: 李小明 学号: 0003 姓名: 张三丰

显然,需要逐个获取集合中的键值对了。

除了for循环外,Go语言还提供了range关键字。与for结合,也可以实现循环遍历,其使用格式如下:

go
复制代码for index, value := range variable {
    // 循环体
}

其中,index表示索引或键的值;value表示元素的值;variable表示数组、切片或集合变量;由大括号包裹的部分是循环体,可以使用index和value变量。

对于本例,可以如下实现:

go
复制代码for key, value := range studentInfos {
   fmt.Println("学号:", key, "姓名:", value)
}

再次运行,即可得到所要求的输出结果了。

这种for与range结合实现循环遍历的结构,也被称为for-range结构。这种结构同样适用于数组和切片。

# 小结

🎉 恭喜,您完成了本次课程的学习!

📌 以下是本次课程的重点内容总结:

  1. 获取数组、切片和集合的元素个数
  2. 获取和修改数组、切片和集合的元素的值
  3. 数组、切片和集合的循环遍历

在实际使用数组、切片和集合时,往往是综合本讲和上一讲内容中的知识点一起使用的。

比如,在使用for循环遍历数组时,需要首先获取元素个数,然后将其作为循环结束的条件。否则,将会引发下表越界错误,导致程序出错。

再比如,使用for-range循环,再循环体内使用元素的值参与其它运算,抑或是根据元素值或集合中键的值进行元素筛选,等等。

➡️ 在下次课程中,我们会介绍如下内容:

  • Go语言中的“函数”,包括:
    • 函数的定义
    • 函数的调用