golang循环结构

循环语句

在不少实际问题中有许多具有规律性的重复操作,因此在程序中就需要重复执行某些语句。

以下为大多编程语言循环程序的流程图:

在这里插入图片描述

for循环

基本语法

GO为了精简关键字,没有提供while循环,所有的循环都通过for循环实现。

基本语法

for init; condition; post { }

for 初始化;迭代条件;步进操作{
    循环体
}

参数解释

【init】
 一般为赋值表达式,给控制变量赋初值;
 
【condition】
关系表达式或逻辑表达式,循环控制条件;

【post】
一般为赋值表达式,给控制变量增量或减量。

执行流程

1、先对表达式 1 赋初值;

2、判别赋值表达式 init 是否满足给定条件,若其值为真,满足循环条件,则执行循环体内语句,然后执行 post,进入第二次循环,再判别 condition;否则判断 condition 的值为假,不满足条件,就终止for循环,执行循环体外语句。

举例

package iterative

import "fmt"

func IterativeFor(){
	//for循环
	for i:=0;i<=10;i++{
		if(i%2==0){
			fmt.Printf("数字%d是偶数\n",i)
		}
	}
}

输出结果

数字0是偶数
数字2是偶数
数字4是偶数
数字6是偶数
数字8是偶数
数字10是偶数

省略条件

for循环的三个条件可以组合使用,例如省略一项或多项达到别的效果。

初始赋值项
package iterative

import "fmt"

func IterativeFor(){
	//for循环
	for i:=0;i<=10;i++{
		if(i%2==0){
			fmt.Printf("数字%d是偶数\n",i)
		}
	}
    //报错
    fmt.Println(i)
}

直接在for循环中初始化的变量的作用域局限于该for循环,不能在外部访问。

语法

//省略掉了赋值项
//初始值将从外部寻找
for ;condition;post{
    
}

举例

package iterative

import "fmt"

func IterativeFor(){
	//省略初始赋值
	age :=15
	for ;age<=18;age++{
		if(age<18){
			fmt.Printf("您才%d岁,尚未成年,请快快长大\n",age)
		}else{
			fmt.Printf("恭喜您,您已经%d了,可以去上班了\n",age)
		}
	}
}

结果

您才15岁,尚未成年,请快快长大
您才16岁,尚未成年,请快快长大
您才17岁,尚未成年,请快快长大
恭喜您,您已经18了,可以去上班了

优势:变量可以在for循环外访问

迭代条件

如果省略了迭代条件将陷入死循环,此时需要使用break显式跳出。

语法

//省略掉了迭代条件
//for循环将不知何时退出循环,所以会陷入死循环
for init;;post{
    
}

举例

package iterative

import "fmt"

func IterativeFor(){

	//省略迭代条件
	for i:=0;;i++{
		if(i%2==0){
			fmt.Printf("数字%d是偶数\n",i)
		}
	}
}

跳出死循环

package iterative

import "fmt"

func IterativeFor(){

	//省略迭代条件
	for i:=0;;i++{
		if(i%2==0){
			fmt.Printf("数字%d是偶数\n",i)
		}
        if(i>20){
            //跳出死循环,相当于在内部添加迭代终止条件
            break
        }
	}
}
增量条件

省略增量条件也将陷入死循环,因为迭代对象不发生改变,永远无法满足迭代终止条件。

语法

//省略掉了增量条件
//for循环无法达到迭代终止条件,陷入死循环
for init;condition;{
    
}

要终止死循环,只能在循环内部添加增量条件。

举例

package iterative

import "fmt"

func IterativeFor(){

	//省略增量条件
	for i:=0;i<20; {
		if(i%2==0){
			fmt.Printf("数字%d是偶数\n",i)
		}
	}
}

跳出死循环

package iterative

import "fmt"

func IterativeFor(){

	//省略迭代条件
	for i:=0;i<=10;{
		if(i%2==0){
			fmt.Printf("数字%d是偶数\n",i)
		}
        //添加增量条件
        i++
	}
}
分号的省略
1.初始值是一个赋值语句
2.迭代终止条件是一个布尔表达式
3.增量条件是一个步进表达式

由于三项非常容易区别,所以如果我们省略了哪一项,他后面对应的;也可以省略。

等价

等价于while

和其他语言的while一样:

for condition {
	
}

//带分号识别度更高
for ;condition; {
    
}

等价于死循环

for ;;;{
    
}

for true {
    
}

//把分号都省略了
for {
    
}

for…range

相当于js中的for...of和python中的for...in.

Go 语言中 range 关键字用于 for 循环中迭代整型(int)、数组(array)、切片(slice)、通道(channel)、字符串(string)或集合(map)的元素。

在数组和切片中它返回元素的索引和索引对应的值,在集合中返回 key-value 对。

基本语法

for key, value := range oldMap {
    newMap[key] = value
}

以上代码中的 key 和 value 是可以省略。

如果只想读取 key,格式如下:

for key := range oldMap
//或者
for key, _ := range oldMap

如果只想读取 value,格式如下:

for _, value := range oldMap

使用范围及含义

适用范围keyvaluedesc
int整数类型从0到该整数从Go1.22新增支持
string字符串类型索引
array数组类型索引
slice切片类型索引
map映射类型
chan通道类型从通道接收的值

实例举例

迭代整型
package iterative

import "fmt"

func IterativeRange(){
	//适用整数
	for v := range 5 {
		fmt.Println(v)
	}
}

结果

0
1
2
3
4

注意

这种方式目前局限性很大,默认只能实现[0,n),如果我们要实现[m,n),可以使用手动过滤,例如:

package iterative

import "fmt"

func IterativeRange(){
    //我们要实现[5,10)
	for v := range 10 {
        //手动过滤不满足条件的
        if(v<5){
            continue
        }
		fmt.Println(v)
	}
}
迭代数组和切片

因为数组和切片在用这种迭代时基本一致。

package iterative

import "fmt"

func IterativeRange(){
	//适用于数组
	var arr = [5]string{"a","b","c","d","e"}
	for key,val := range arr{
		fmt.Printf("数组arr的索引为%d,值为%s\n",key,val)
	}

	//适用于切片
	var slice1 = []string{"a","b","c","d","e"}
	for key,val := range slice1{
		fmt.Printf("切片slice1的索引为%d,值为%s\n",key,val)
	}
}

结果

数组arr的索引为0,值为a
数组arr的索引为1,值为b
数组arr的索引为2,值为c
数组arr的索引为3,值为d
数组arr的索引为4,值为e
切片slice1的索引为0,值为a
切片slice1的索引为1,值为b
切片slice1的索引为2,值为c
切片slice1的索引为3,值为d
切片slice1的索引为4,值为e
迭代字符串

range 迭代字符串时,返回每个字符的索引和 Unicode 代码点(rune)。

package iterative

import "fmt"

func IterativeRange(){
	//迭代字符串
	var str1 string= "hello 人类!"
	for key,val :=range str1{
		fmt.Printf("该字符位于字符串的下标%d,rune值为%v,字符值为%c\n",key,val,val)
	} 
}

结果

该字符位于字符串的下标0,rune值为104,字符值为h
该字符位于字符串的下标1,rune值为101,字符值为e
该字符位于字符串的下标2,rune值为108,字符值为l
该字符位于字符串的下标3,rune值为108,字符值为l
该字符位于字符串的下标4,rune值为111,字符值为o
该字符位于字符串的下标5,rune值为32,字符值为 
该字符位于字符串的下标6,rune值为20154,字符值为人
该字符位于字符串的下标9,rune值为31867,字符值为类
该字符位于字符串的下标12,rune值为33,字符值为!

注意

直接得到的值是数字编码,操作时可能需要将数字编码转换成对应字符。

迭代映射map
package iterative

import "fmt"

func IterativeRange(){
	//迭代map
	map1 := map[string]string{
		"a":"apple",
		"b":"bear",
		"c":"cat",
	}
	for key,val := range map1 {
		fmt.Printf("map的键为%s,值为%s\n",key,val)
	}
}

结果

map的键为a,值为apple
map的键为b,值为bear
map的键为c,值为cat
迭代遍历通道

range 遍历从通道接收的值,直到通道关闭。

ch := make(chan int, 2)
ch <- 1
ch <- 2
close(ch)
for key := range ch {
    fmt.Println(key)
}

结果

1
2
### 解决 Golang 循环导入依赖问题 在 Go 语言中,循环导入是指两个包相互之间直接或间接地互相导入。这会导致编译错误,因为Go不允许这种结构存在。 当遇到循环导入时,可以考虑重构代码来打破这个循环关系。以下是几种常见的解决方案: #### 方法一:提取公共接口到新包 如果 A 和 B 包之间有循环依赖,则可以在另一个独立的 C 包里定义它们共有的接口或数据类型[^1]。 ```go // c/common.go package common type CommonInterface interface { DoSomething() } ``` 然后让 `A` 和 `B` 都只依赖于 `common` 而不是彼此。 #### 方法二:使用指针或接口代替具体实现 有时可以通过引入抽象层(如接口),使得一方不再需要知道另一方的具体细节[^3]。 ```go // b/b.go package b import ( "a" ) func New() *Handler { h := &Handler{} // 使用接口而非具体的 a.Type 类型 var t a.InterfaceType = new(a.ConcreteType) h.SetDependency(t) return h } type Handler struct { dep InterfaceType } func (h *Handler) SetDependency(d InterfaceType) { h.dep = d } ``` 这样即使 `b.New()` 函数内部创建了一个来自 `a` 的对象实例,由于它只是作为参数传递给其他方法而不是被声明为字段成员,因此不会造成实际的导入路径上的冲突。 #### 方法三:延迟初始化 对于某些情况下的循环引用,可能只需要其中一个方向的实际调用发生在运行期而不是编译期间即可解决问题。此时可采用工厂模式或其他形式的懒加载机制,在真正需要用到对方模块的功能之前不执行任何可能导致立即求值的操作[^2]。 例如通过函数返回匿名函数的方式来进行回调注册: ```go // a/a.go package a var callback func() func RegisterCallback(cb func()) error { if cb != nil { callback = cb return nil } else { return errors.New("callback cannot be nil") } } ``` 而对应的接收端则只需提供相应的处理逻辑而不必关心谁会触发这些行为: ```go // main/main.go package main import ( _ "b" // 注意这里使用了空白标识符 _ "fmt" ) func init() { err := a.RegisterCallback(func() { fmt.Println("Called from package 'a'") }) if err != nil { log.Fatal(err) } } ``` 以上三种方式都可以有效地帮助开发者应对不同场景下可能出现的循环导入难题。选择哪种方案取决于项目的具体情况以及设计需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值