写点什么

Go 学习笔记——Only For

作者:为自己带盐
  • 2022 年 4 月 09 日
  • 本文字数:2634 字

    阅读完需:约 9 分钟

Go 学习笔记——Only For

书接上回《条件判断if》

先诉诉苦

这篇更文,本来再 3 月 15 号就差不多快写好了,然后就在草稿箱一直趴到了现在。。。直到今天才又翻出来,重新过了一遍,稍加修改,才重新发布。

这 1 个月,真的太难熬了,一直在疯狂的赶项目,日赶夜赶,有点闲功夫也要首先安排在陪家人,陪孩子上,根本顾不上其它。我一直在 infoq 微信群里看到大家比拼更文,也在别的渠道,看到大家都在积极的参与社区活动,很是羡慕。我觉得这种活动真的能让人感到感到很青春,可以从乏味的业务逻辑中解脱出来,放飞自己,无所顾忌的交流和成长。但真的很遗憾,我错过了很多这种机会,而且近期可能还会有相当一段时间处于这种状态。。。

再说说为什么要坚持记笔记的形式来更文,我从第一篇关于 Go 的提到过,我对 Go 真的是完全的从零开始,记笔记也是临时起意,主要还是为了加深自己的记忆,工作有时候轻松有时忙,轻松的适合,记得就多一些,忙的时候可能就少,而且再回来继续“连载”,可能又会对之前记过学过的知识有一点遗忘,所以我会一直记,最差今年也会把 TonyBai 老师的这篇 Go 的入门教程过完(额。。但应该不会到真到年底,那也真的太差劲了,哈哈)

好了,说是诉苦,好像是在给自己找了点冠冕堂皇的断更接口,继续!

经典形式

在 99.999%的编程语言中,都会包含 go 循环控制语句,实现形式也大同小异,go 中就是这样

var sum intfor i := 0; i < 10; i++ {    sum += i}println(sum)
复制代码

所以关于偏概念性的内容我就不多记了,这里只记录几个关键的点

  1. 在经典的 go 语句中,包含 4 个部分,循环前置语句(循环开始前执行一次),条件判定表达式(决定循环是否继续下去),循环体(通过条件判定后要做的事儿),循环后置语句(循环体每次执行完成后要做的事儿)

  2. 1 中除了循环体之外,其余都是可选的,这点和其他语言也类似,但要注意符号不能省这是语法要求,但但还要注意,有一种情况连符号也可以省~

//情况一,省略前置条件,但前置条件和条件判定之间的';'不可以省i := 0for ; i < 10; i++ {    println(i)    }
//情况二,前置和后置循环条件都省了(后置挪到了循环体里),';'就可以省掉了i := 0for i < 10 { i++ println(i) }//情况三,前置,后置,条件判定都不要了,也就是无限循环,即默认判定条件始终为truefor { //do something..}
复制代码
  1. Go 语言的 for 循环支持声明多循环变量,并且可以应用在循环体以及判断条件中(这个就不举例子了,实际代码当中,我个人不太建议这么写,会有点别扭,但是是合理的,只是个人习惯)

特色形式

for range

for range 是 go 语言中的一种语法糖,完全可以用经典模式来替代,但这种形式非常常用,也为了提高代码的观感,所以衍生出来的这样一种形式(和 C#语言中的 foreach 异曲同工)

var sl = []int{1, 2, 3, 4, 5}//经典模式for i := 0; i < len(sl); i++ {		fmt.Printf("sl[%d] = %d\n", i, sl[i])}//for range模式for i, v := range sl {  fmt.Printf("sl[%d] = %d\n", i, v)}
复制代码



打印结果都是完全一致的。

for range 的形式,也有特殊情况的变种

a.当我们不关心元素的值时,我们可以省略代表元素值的变量 v,只声明代表下标值的变量 i

for i := range sl {  // ... }
复制代码

b.如果我们不关心元素下标,只关心元素值,那么我们可以用空标识符替代代表下标值的变量 i。这里一定要注意,这个空标识符不能省略,否则就与上面的“变种一”形式一样了,Go 编译器将无法区分。(go 语言里的空标识符,真是妙用多多)

for _, v := range sl {  // ... }
复制代码

c.如果我们既不关心下标值,也不关心元素值时,上面例子的 v:也可以写成空标识符,但这种问题我们都能想到,go 的创始人们怎么会想不到,所以他们提供了更优雅的形式

//可以这样for _, _ = range sl {  // ... }
//但最好还是这样for range sl { // ... }
复制代码

与符合数据类型或 string 类型的结合

  1. for range 对于 string 类型来说,每次循环得到的 v 值是一个 Unicode 字符码点,也就是 rune 类型值,而不是一个字节,返回的第一个值 i 为该 Unicode 字符码点的内存编码(UTF-8)的第一个字节在字符串内存序列中的位置。(所以,for 和 for range 对字符串类型的值做循环带来的意义是不一样的)

var ss = "中国人"	for i, v := range ss {		fmt.Printf("%d %s 0x%x\n", i, string(v), v)	}
复制代码


  1. 在 Go 语言中,我们要对 map 进行循环操作,for range 是唯一的方法,for 经典循环形式是不支持对 map 类型变量的循环控制的。如下边的例子,每次循环 map,k,v 会被分别赋予 map 中的 key 和 value 值,这也是 go 独家提供的一点,在写起来的时候会更加自然

var m = map[string]int {  	"Rob"  : 67,    "Russ" : 39,    "John" : 29,}
for k, v := range m { println(k, v)}
复制代码
  1. 除了可以针对 string、数组 / 切片,以及 map 类型变量进行循环操作控制之外,for range 还可以与 channel 类型配合工作,而我现在还不了解 channel,所以这里就先不细记了,先知道它可以和 for range 配合就好。

  2. label,continue,break

continue,break 和其他语言的含义基本一样,continue 是跳出本次循环,break 是中断循环

而带了 label 会让 continue 和 break 变得更加灵活

比如用 label 和 continue 配合,可以实现 break 的效果

var sl = [][]int{		{1, 34, 26, 35, 78},		{3, 45, 13, 24, 99},		{101, 13, 38, 7, 127},		{54, 27, 40, 83, 81},	}	//1	outerloop:	for i := 0; i < len(sl); i++ {		for j := 0; j < len(sl[i]); j++ {			if sl[i][j] == 13 {				fmt.Printf("found 13 at [%d, %d]\n", i, j)				continue outerloop			}		}	}
//2 for i := 0; i < len(sl); i++ { for j := 0; j < len(sl[i]); j++ { if sl[i][j] == 13 { fmt.Printf("found 13 at [%d, %d]\n", i, j) break } } }//1这段其实就是通过continue和outerloop标签,跳出内层循环并继续外层循环//但其实效果和中断内层循环效果是一样的,所以就可以直接换成2,结果是一样的
复制代码


而 break 和 label 配合,上面的例子 1 中的 continue 换成 break 后,就直接把外层循环也中断了。


最后,for 语句在日常使用中有一些常见的问题需要你格外注意,包括循环变量重用、range 表达式副本参与循环、map 类型遍历的随机性,等等我也不多记了。

其实 for 还是很容易理解的,但要在日常编码中做到不犯错却并不太容易,所以尽管 tonybai 老师在课程最后提醒了几个关于 for 和 for range 的坑,我觉得在掌握了 go 语言里循环的特性之后,剩下的就是实践了,坑来不是祸,栽过就是福。

好了,今天就到这!

发布于: 刚刚阅读数: 5
用户头像

学着码代码,学着码人生。 2019.04.11 加入

狂奔的小码农

评论

发布
暂无评论
Go 学习笔记——Only For_Go_为自己带盐_InfoQ写作平台