go语言学习笔记 — 基础 — 控制流:for循环语句

Posted Locutus

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了go语言学习笔记 — 基础 — 控制流:for循环语句相关的知识,希望对你有一定的参考价值。

在不少实际问题中,有许多具有规律性的重复操作,因此在程序中就需要重复执行某些语句。以下为大多数编程语言循环程序的流程图:

Go语言的循环只有for一种表示方法,提供了以下两种循环处理语句:

循环类型描述
for循环重复执行语句块
for嵌套循环在for循环中嵌套一个或多个 for 循环

for循环可以通过break、goto、return、panic等语句强制退出循环。


1. for循环

for循环是一个循环控制结构,可以用来执行指定次数的循环。

1.1 for循环的语法

  • go中for循环的一般形式(重要)

与C语言一样,go语言的循环只有一种形式,在for语句之间使用分号分隔。

for 初始语句; 条件表达式; 结束语句 {
    循环体
}

循环体不停地进行循环,直到条件表达式返回false时,自动退出循环,接着执行for的“}”之后的语句。

  • for中的初始语句 —— 开始循环时执行的语句

初始语句一般为赋值表达式,在第一次循环前执行,一般用于初始化控制变量。控制变量的作用域被限定在这个for循环之内。初始语句可以忽略不写,但初始语句之后的分号;必须写。

step := 2
for ;step > 0; step-- {
    fmt.Println(step)
}

控制变量step放在for循环之前进行初始化,并且省略了初始化语句,此时控制变量step的作用域比在初始化语句中声明step的要大。

  • for中的条件表达式 —— 控制是否循环的开关

条件表达式为关系表达式或逻辑表达式,是循环控制的判断条件。每次for循环开始前,计算条件表达式。如果值为true,则循环继续,否则循环结束。

a. 与C语言的for(;;)一样,下面代码忽略初始语句、条件表达式,但是保留结束语句。此时for循环持续执行(死循环),每次循环结束都会调用结束语句i++,循环的退出需要在循环体内调用break语句。

for ; ; i++ { 
    循环体
}

示例:当i大于10时,通过break语句跳出for循环,执行循环之外的语句。

var i int

for ; ; i++ {     // 没有设置i的初始值
    if i > 10 {
        break
    }
    
    // code block
}

// code block

b. 无限循环(死循环)

条件表达式可以被忽略,忽略条件表达式的for循环默认形成无限循环。无限循环在收发处理中较常见,但需要有可控的方式结束循环,如break语句。

for { 
    循环体
}

把i++从for结束语句位置放到函数体末尾是等效的,这样的代码更有可读性。

var i int  // 全局变量声明,下方赋值

for {
    if i > 10 {
        break
    }
    
    // code block
    
    i++
}

c. 只有一个条件的for循环(将if判断整合到for中)

for condition {
    循环体
}

下面代码与C语言的while类似,在for后添加一个条件表达式,满足条件时持续循环,否则结束循环。

在代码第3行中,将之前使用if i > 10 {}的判断表达式进行取反,变为判断i小于等于10时持续进行循环。

var i int  // 全局变量声明,初始化为零值

for i <= 10 {
    fmt.Println(i)
    i++
}
  • for中的结束语句 —— 每次循环结束前执行的语句

结束语句一般为赋值表达式,给控制变量增量或减量。在结束每次循环前,都要调用结束语句;如果for循环被break、goto、return、panic等语句强制退出,就不会执行结束语句,如i++。

1.2 for循环的执行过程

以C语言风格的for循环为例

  • 先对初始表达式赋初值
  • 接着判断初始表达式是否满足条件表达式,若判别值为真(满足循环条件),则先执行循环体内语句再执行结束语句;然后进入下一次循环,判别条件。若判别值为真,仍是先执行循环体内语句再执行结束语句;若判别值为假,不满足循环条件,就终止for循环,不再执行结束语句,改为执行循环体外的语句。

package main

import "fmt"

func main() {
	var b int = 15
	var a int

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

	// for循环
	// 第一种形式
	for a := 0; a < 10; a++ {
		fmt.Printf("a 的值为: %d\\n", a)
	}

	// 第二种形式
	// a还是默认的int,初始是零值
	for a < b {
		a++
		fmt.Printf("a 的值为: %d\\n", a)
	}

    // 没有元素的序号位要补0
	for i,x := range numbers {
		fmt.Printf("第 %d 位 x 的值å = %d\\n", i,x)
	}
}

2. for嵌套循环

go语言允许用户在循环体内使用循环。go语言嵌套循环的格式:

for [condition | (init; condition; increment) | range] {
   for [condition | (init; condition; increment) | range] {
      statement(s);
   }
   statement(s);
}

输出2到100间的素数

package main

import "fmt"

func main() {
	// 定义局部变量
	var i, j int

	for i = 2; i < 100; i++ {
		for j = 2; j <= ( i/j ); j++ {
			if i%j == 0 {
				break   // 如果发现因子,则不是素数
			}
		}
		if j > (i/j) {
			fmt.Printf("%d  是素数\\n", i)
		}
	}
}

输出9*9乘法表

package main

import "fmt"

func main() {
	for i := 1; i <= 9; i++ {       // 遍历行,决定处理第几行。i控制行,以及计算的最大值
		for j := 1; j <= i; j++ {     // 遍历列,决定处理对应行的哪些列。j控制每行的计算列数
			fmt.Printf("%d*%d=%d ", j, i, j*i) // 每个计算式后边空出一格,便于罗列计算式
		}

		fmt.Println("")               // 每执行完一行的所有列计算,就换行
	}
}

/*
i = 1,i < 9,所以执行嵌套的循环
j = 1,i = 1,j <= i,所以执行fmt.Printf("%d*%d=%d ", j, i, j*i),1*1=1
j++,j=2 , i = 1,j <= i不成立,结束嵌套内的循环,所以执行fmt.Println(""),换行

i++,i = 2,i < 9,所以执行嵌套的循环
j = 1,i = 2,j <= i,所以执行fmt.Printf("%d*%d=%d ", j, i, j*i),1*2=2
j++,j = 2,i = 2,j <= i,fmt.Printf("%d*%d=%d ", j, i, j*i),2*2=2
j++,j = 3 , i = 2,j <= i不成立,结束嵌套内的循环,所以执行fmt.Println(""),换行

...

*/

3. 键值循环(for range)一一 直接获得对象的索引和数据

使用for range遍历对象,直接获得对象的索引和数据。使用 for range遍历数组、切片、字符串、map及通道(channel)。通过for range遍历的返回值有一定的规律。

  • for range遍历数组、切片、字符串,返回索引和值

遍历数组、切片

for key, value := range []int{1, 2, 3, 4} {
    fmt.Printf("key:%d value:%d\\n", key, value)
}

遍历字符串时,key和value分别代表字符串的索引和字符串中的每一个字符

var str = "hello 你好"

for key, value := range str {
    fmt.Printf("key:%d value:0x%x\\n", key, value)
}

value变量的实际类型是rune,即int32,以十六进制打印出来就是字符的编码。

  • for range遍历字典map,返回键和值
func TestMapAddVisit(t *testing.T) {
	scene := make(map[string]int) 
	t.Log(scene)

	scene["route"] = 66 
	scene["brazil"] = 4
	scene["china"] = 960
	t.Log(scene)

	// 使用for range,迭代map,遍历键值对
	for k, v := range scene { 
		t.Log(k, v)
	}

	for _, v := range scene { 
	    t.Log(v)
	}

	for k := range scene {
	    t.Log(k)
	}

}

直接使用for range遍历map时,我们可以同时获得键key和值value。注意,遍历map时,输出的键值对是无序的,如果需要有序的键值对输出,需要对结果进行排序。

for k, v := range scene { 
    t.Log(k, v)
}
  • for range遍历通道(channel),只返回通道内类型对应的数据
package main

import "fmt"

c := make(chan int) // 创建通道c

// 并发执行
go func () {
    c <- 1    // 向通道c中添加数据
    c <- 2    // 向通道c中添加数据
    c <- 3    // 向通道c中添加数据
    close(c)  // 关闭通道
}()           // 在声明goroutine结束后,马上并发执行

// 使用for range对通道c进行遍历,即不断地从通道中取数据, 直到通道被关闭
func main () {
    for v := range c {
        fmt.Println(v)
    }
}
  • 在遍历对象时,选择希望获得的变量(使代码更简单)

匿名变量_可以理解为一种占位符。匿名变量变量不会进行内存空间分配,也不会占用一个变量的名字。在for range中,可以对 key使用匿名变量,也可以对value使用匿名变量。

只遍历值,把不需要的键改为匿名变量_的形式,可以使用如下形式:

for _, v := range scene { 
    t.Log(v)
}

只遍历键,无须把值改为匿名变量_的形式,忽略v即可。

for k := range scene {
    t.Log(k)
}    

4. 总结

  • Go语言的for循环包含初始化语句、条件表达式、结束语旬, 这3个部分均可缺省
  • for range支持对数组、切片、字符串、map、通道进行遍历操作
  • 在需要时,可以使用匿名变量_,对for range变量的键值进行选取

以上是关于go语言学习笔记 — 基础 — 控制流:for循环语句的主要内容,如果未能解决你的问题,请参考以下文章

go语言学习笔记 — 基础 — 控制流:流程控制

go语言学习笔记 — 基础 — 控制流:goto跳转语句

go语言学习笔记 — 基础 — 控制流:if条件(分支)语句

go语言学习笔记 — 基础 — 控制流:switch分支选择语句

GO语言学习——Go语言基础之流程控制二

go-008-循环语句