Go基础编程 - 12 -流程控制

news2024/11/15 8:39:45

流程控制

  • 1. 条件语句
    • 1.1. if...else 语句
    • 1.2. switch 语句
    • 1.3. select 语句
      • 1.3.1. select 语句的通信表达式
      • 1.3.2. select 的基特性
      • 1.3.3. select 的实现原理
      • 1.3.4. 经典用法
        • 1.3.4.1 超时控制
        • 1.3.4.2 多任务并发控制
        • 1.3.4.3 监听多通道消息
        • 1.3.4.4 default 实现非堵塞读写
  • 2. 循环语句
    • 2.1. for 语句
    • 2.2. for ... range
  • 3. goto、break、continue

上一篇:函数
下一篇:单元测试


1. 条件语句

Go语言中,条件语句分为三种:if语句、switch语句和select语句。

1.1. if…else 语句

if语句基本格式如下:

if 条件 {
    // 条件满足时执行的语句
} else if 条件 {
    // 条件满足时执行的语句
} else {
    // 条件为false时执行的语句
}

不支持三元操作符(三目运算符) :“a > b ? a : b”

1.2. switch 语句

基于不同的条件执行不同的动作,每个case分支都是唯一的,从上至下逐一匹配,直到匹配到一个case分支,执行该分支的代码,并终止匹配。

switch语句基本格式如下:

switch{
case1:
    // 值等于值1时执行的语句
case2:
    // 值等于值2时执行的语句
default:
    // 值不等于值1和值2时执行的语句
}
  • 如果switch没有表达式,它会匹配true。
  • case 分支表达式可以是任意类型,不限于常量,但必须是相同类型
  • 一个 case 可以同时测试多个值,用逗号分隔。例如:case val1, val2, val3:
  • 一个 case 分支可以使用fallthrough语句,匹配成功后强制执行相邻的下一个case语句,且fallthrough不可使用在最后一个分支。
  • Go语言的switch默认相当于每个case最后带有break,可省略。

示例:

package main

import "fmt"

func main() {

    var i = 0
    switch i {
    case 0:
        println("0")    
        println("fallthrough")  // 使用fallthrough语句,匹配成功后强制执行下一个case代码:case 1,2
        fallthrough
    case 1, 2:  // 一个 case 可以同时测试多个值,用逗号分隔。
        fmt.Println("1 或 2")
    case 3:
        fmt.Println("3")
    default:
        fmt.Println("default")
        // fallthrough  // fallthrough不可使用在最后一个分支
    }
    // 输出结果:
    // 0
    // fallthrough
    // 1 或 2

    var n = 6
    switch { //省略条件表达式默认为true,可当 if...else if...else
    case n > 0 && n < 10:
        fmt.Println("i > 0 and i < 10")
    case n > 10 && n < 20:
        fmt.Println("i > 10 and i < 20")
    default:
        fmt.Println("def")
    }
    // 输出结果:
    // i > 0 and i < 10
}

1.3. select 语句

select是Go中的一个控制结构,类似 switch 语句,用于处理异步IO操作。但是select用于等待多个通信操作的完成,会随机执行一个可运行的case;如果没有case可运行,它将阻塞,直到有case可运行。

1.3.1. select 语句的通信表达式

  • 一个通信操作,如:ch <- vv := <-ch
  • 一个接收表达式,如:v := <-ch
  • 一个发送表达式,如:ch <- v
  • 一个默认通信,如:default

1.3.2. select 的基特性

  • case 语句必须是一个 cannel 操作,要么是发送,要么是接收。
  • select 中 default 语句总是可执行的(一般不写在里面,因为会很消CPU资源)。
  • 如果任意某个通信可以执行,它就执行;其它被忽略。
  • 如果有多个 case 都可以运行,select 会随机公平地选出一个执行;其它不会执行。
  • 如果没有可运行的 case 语句,且有 default 语句,那么就会执行 default 的动作。
  • 如果没有可运行的 case 语句,且没有 default 语句,select 将阻塞,直到某个 case 通信可以运行。

1.3.3. select 的实现原理

参考:Go select 底层原理、select 的随机公平策略理

在这里插入图片描述

无 case 永久堵塞

select{} 

// fatal error: all goroutines are asleep - deadlock!
// 
// goroutine 1 [select (no cases)]:

select 所有 case 均无法执行且没有 default,则阻塞

ch := make(chan struct{})
select {
case data <- ch:  // 只有一个 case,实际会被编译器转换为相应 channel 相应的收发操作,其实和实际调用 data := <- ch 并没有什么区别
    fmt.Printf("ch data: %v\n", data)
}

// fatal error: all goroutines are asleep - deadlock!
// 
// goroutine 1 [chan receive]:

select多个case同时可以执行,随机选择一个去执行

package main

import "fmt"

func main() {
	var c1 = make(chan int, 2)
	c2 := make(chan string, 2)
	c3 := make(chan int, 2)
	var i1 int
	i2 := "two"

	c1 <- 1
	c2 <- "one"
	c3 <- 3

	select {
	case i1 = <-c1:
		fmt.Printf("received %d from c1\n", i1)
	case c2 <- i2:
		fmt.Printf("send %s to c2\n", i2)
	case i3, ok := <-c3: // same as: i3, ok := (<-c3)
		if ok {
			fmt.Printf("received %d from c3\n", i3)
		} else {
			fmt.Printf("c3 is closed\n")
		}
	default:
		fmt.Printf("no communication\n")
	}
}
//随机输出下面一条:
// send two to c2
// received 1 from c1
// received 3 from c3

1.3.4. 经典用法

1.3.4.1 超时控制
package main

import (
   "fmt"
   "time"
)

func main() {
    ch := make(chan int)
    go func() {
        time.Sleep(3 * time.Second)
        ch <- 1
    }()

    select {
    case data, ok := <-ch:
        if ok {
            fmt.Println("接收到数据: ", data)
        } else {
            fmt.Println("通道已被关闭")
        }
    case <-time.After(2 * time.Second):
        fmt.Println("超时了!")
    }
}

// 超时了!
1.3.4.2 多任务并发控制
package main

import (
	"fmt"
)

func main() {
	ch := make(chan int)

	for i := 0; i < 10; i++ {
		go func(id int) {
			ch <- id
		}(i)
	}

	for i := 0; i < 10; i++ {
		select {
		case data, ok := <-ch:
			if ok {
				fmt.Println("任务完成:", data)
			} else {
				fmt.Println("通道已被关闭")
			}
		}
	}
}

// 每次执行,顺序不一致
// 任务完成: 2
// 任务完成: 0
// 任务完成: 1
// 任务完成: 4
// 任务完成: 3
// 任务完成: 6
// 任务完成: 5
// 任务完成: 7
// 任务完成: 8
// 任务完成: 9
1.3.4.3 监听多通道消息
package main

import (
   "fmt"
   "time"
)

func main() {
   ch1 := make(chan int)
   ch2 := make(chan int)

   // 开启 goroutine 1 用于向通道 ch1 发送数据
   go func() {
      for i := 0; i < 5; i++ {
         ch1 <- i
         time.Sleep(time.Second)
      }
   }()

   // 开启 goroutine 2 用于向通道 ch2 发送数据
   go func() {
      for i := 5; i < 10; i++ {
         ch2 <- i
         time.Sleep(time.Second)
      }
   }()

   // 主 goroutine 从 ch1 和 ch2 中接收数据并打印
   for i := 0; i < 10; i++ {
      select {
      case data := <-ch1:
         fmt.Println("Received from ch1:", data)
      case data := <-ch2:
         fmt.Println("Received from ch2:", data)
      }
   }

   fmt.Println("Done.")
}
1.3.4.4 default 实现非堵塞读写
import (
   "fmt"
   "time"
)

func main() {
   ch := make(chan int, 1)

   go func() {
      for i := 1; i <= 5; i++ {
         ch <- i
         time.Sleep(1 * time.Second)
      }
      close(ch)
   }()

   for {
      select {
      case val, ok := <-ch:
         if ok {
            fmt.Println(val)
         } else {
            ch = nil
         }
      default:
         fmt.Println("No value ready")
         time.Sleep(500 * time.Millisecond)
      }
      if ch == nil {
         break
      }
   }
}

2. 循环语句

2.1. for 语句

Go语言 for 循环有 3 种形式。

for init; condition; post{}
for condition {}
for {}

示例:

package main

import "fmt"

func main() {

    for i := 0; i < 10; i++ {
        fmt.Println("i =", i)
    }
    fmt.Println()

    n := 10
    for n > 0 {
        fmt.Println("n =", n)
        n--
    }
    fmt.Println()

    j := 0
    for {
        if j >= 10 {
            break
        }
        fmt.Println("j =", j)
        j++
    }
    fmt.Println()
}

2.2. for … range

range 类似迭代器操作,返回(索引, 值)或(键, 值)。

for ... range 格式可以对 slice、map、数组、字符串等进行迭代循环。格式如下:

for key, value := range oldMap {

}

示例:

package main

import "fmt"

func main() {
    s := "string中文"

    for i, v := range s {
        fmt.Printf("%d, %d, %T\n", i, v, v)
    }
    println()

    // 忽略值
    for i := range s {
        fmt.Printf("%d\n", i)
    }

    // 忽略返回值,仅迭代
    for range s {

    }

    println()
    println()

    a := [3]int{0, 1, 2}
    for i, v := range a {
        if i == 0 {
            a[1], a[2] = 222, 333 // range 会复制对象,修改 a 不影响 i, v 的值
        }
        fmt.Println(i, v, a) // 输出:[0 222 333]
        a[i] = 100 + v
    }
    fmt.Println(a) // 输出:[100 101 102]

    println()
    println()

    si := []int{1, 2, 3, 4, 5}
    for i, v := range si {
        if i == 0 {
            si = si[:3] // 对 slice 的修改,不会影响 range
            si[2] = 100 // 对底层数据的修改,影响 range
        }

        fmt.Printf("%d, %d, %v\n", i, v, si)
    }
}

for...range 可以遍历 channel, 与遍历 map、slice 不同

package main

import "fmt"

func main() {
    queue := make(chan string, 2)
    
    for i := 0; i < 10; i++ {
		 queue <- "-data-" + strconv.Itoa(i)
	}
    close(queue)

    // 这个 `range` 迭代从 `queue` 中得到的每个值。
    // 因为我们在前面 `close` 了这个通道,这个迭代会在接收完 queue 中的值之后结束;
    for elem := range queue {
        fmt.Println(elem)
    }
    // 如果我们没有 `close` 它,我们将在这个循环中继续阻塞执行,等待接收下一个个值。类似下面代码 for {}
    for {
    	fmt.Println(<- queue)
    }
}

3. goto、break、continue

break:跳出循环。
continue:跳过当前循环,继续下一次循环。仅限 for 循环内使用。
goto:通过label跳转到指定位置。

  • 三个标签都可以配合标签(label)使用。
  • continue, break 配合标签可用于多层循环跳出。
  • goto 是调整执行位置,跳到指定标签代码块执行;continue 配合标签为跳到指定循环继续执行, break 配合标签跳出指定标签代码块的循环。
package main

import "Learing/demo"

func main() {
    // 标签区名分大小写,若定义标签不使用则会照成编译错误。
LabelBreak: // break 的跳转标签放在循环语句前面
    for {
        for i := 0; i < 10; i++ {
            if i > 2 {
                break LabelBreak // break, 跳出 LabelBreak 标签代码块,不再执行循环
            }
            fmt.Println("break -", i)
        }
    }
    fmt.Println()

    LableContinue:  // continue 的跳转标签放在循环语句前面
    for i := 0; i < 5; i++ {
        for {
            fmt.Println("before", i)
            continue LableContinue // continue, 跳到LableContinue标签的循环,继续执行循环。
            fmt.Println("after")
        }
    }
    fmt.Println("continue label")
    fmt.Println()

    for {
        for i := 0; i < 10; i++ {
            if i > 2 {
                goto LabelGoto // goto, 跳转到指定标签代码块
            }
            fmt.Println("goto -", i)
        }
    }
LabelGoto:
    fmt.Println("goto label")
    fmt.Println()
}

func SelectBreak() {
    ch := make(chan int, 2)

    go func() {
        for i := 0; i < 10; i++ {
            ch <- i
        }
    }()

Label:
    for {
        select {
        case v, ok := <-ch:
            fmt.Println(ok, v)
            if v == 5 {
                break Label // 在 for 的 select 体中 break 到外层循环
            }
        default:
            fmt.Println("The ch value is not ready.")
            time.Sleep(500 * time.Millisecond)
        }
    }

    fmt.Println("Break for select")
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1942877.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【全网首发】小红书最新引流法:轻松留联系方式卡片,直接上演无举报窗口

大家好&#xff01;我是闷声轻创&#xff01;根据最新消息小红书卡片可能会被禁止掉&#xff0c;这对我们的引流矩阵有真不小的冲击&#xff0c;毕竟小红书作为国内领​先的年轻人的生活分享社区&#xff0c;但上有政策下有对策&#xff0c;我今天发现了一个新的留V的方法&…

七天打造一套量化交易系统:Day2-量化交易策略基本模型及要点

七天打造一套量化交易系统&#xff1a;Day2-量化交易策略基本模型及要点 前期回顾趋势型策略模型原理收益分布重点&#xff1a;什么因素能改进策略&#xff08;截断亏损&#xff0c;让利润奔跑&#xff09;要点总结 均值回复型策略模型原理收益分布重点&#xff1a;避免大额亏损…

去掉roscore的python依赖概述

去掉roscore的python依赖概述 文章目录 去掉roscore的python依赖概述roscore有哪些功能思路关于rosmaster本身及其API的介绍 需要实现的核心API代码实现附录(网图) roscore有哪些功能 启动一个rosmaster节点 调用roslaunch在子进程中&#xff08;popen&#xff09;启动rosmast…

浪潮自研交换机系列常见问题处理

CN61108PC-V-H 不能PING通任何地址&#xff0c;也不能被PING 输入ip traceroute enable既可。注意视图 交换机通过console口远程登录至其他交换机&#xff0c;掉线后console口无法使用 例如有2台交换机A和B&#xff0c;在A交换机上插上console线登录后&#xff0c;在A通过SSH…

【入门教程一】基于DE2-115的My First FPGA 工程

1.1. 概述 这是一个简单的练习&#xff0c; 可以帮助初学者开始了解如何使用Intel Quartus 软件进行 FPGA 开发。 在本章节中&#xff0c;您将学习如何编译 Verilog 代码&#xff0c;进行引脚分配&#xff0c;创建时序约束&#xff0c;然后对 FPGA 进行编程&#xff0c;驱动开…

数据结构:二叉搜索树(简单C++代码实现)

目录 前言 1. 二叉搜索树的概念 2. 二叉搜索树的实现 2.1 二叉树的结构 2.2 二叉树查找 2.3 二叉树的插入和中序遍历 2.4 二叉树的删除 3. 二叉搜索树的应用 3.1 KV模型实现 3.2 应用 4. 二叉搜索树分析 总结 前言 本文将深入探讨二叉搜索树这一重要的数据结构。二…

【Vite】快速入门及其配置

概述 Vite是前端构建工具。vite 相较于webpack,vite采用了不同的运行方式&#xff1a; 开发时&#xff0c;并不对代码打包&#xff0c;而是直接采用ESM的方式来运行项目在项目打包部署时&#xff0c;使用 rollup 对项目进行打包除了速度外&#xff0c;vite使用起来也更加方便…

FPGA-ROM IP核的使用

1.理论 ROM全称&#xff1a;Read-Only Memory&#xff0c;也就是只读型固态半导体存储器&#xff0c;即一旦存储信息&#xff0c;无法再改变&#xff0c;信息也不会因为电源关闭消失。但在FPGA中&#xff0c;实际使用的ROM IP核并不是真正的ROM&#xff0c;其实都是内部的RAM资…

关于企业开展数据资产入表新模式

随着数字化转型持续推进&#xff0c;数据的资产化已成为数字时代不可逆转的趋势。企业数据资产入表已进入倒计时&#xff0c;企业是否科学高效地管理与评估数据&#xff0c;影响着企业是否能够意识到数据应作为资产存在&#xff0c;是否将数据纳入财务报表&#xff0c;并利用数…

【YOLOv5/v7改进系列】引入CoordConv——坐标卷积

一、导言 与标准卷积层相比&#xff0c;CoordConv 的主要区别在于它显式地考虑了位置信息。在标准卷积中&#xff0c;卷积核在输入上滑动时&#xff0c;仅关注局部区域的像素强度&#xff0c;而忽略其绝对位置。CoordConv 通过在输入特征图中添加坐标信息&#xff0c;使得卷积…

探索PyPDF2:Python中的PDF处理大师

探索PyPDF2&#xff1a;Python中的PDF处理大师 1. 背景介绍 在数字化时代&#xff0c;PDF文件因其跨平台的兼容性和内容的稳定性而广受欢迎。然而&#xff0c;处理PDF文件&#xff0c;如合并、分割、提取文本等&#xff0c;往往需要专门的工具。这就是PyPDF2库的用武之地。PyP…

Git报错fatal: detected dubious ownership in repository

报错信息 fatal: detected dubious ownership in repository at 解决办法 一行代码解决 git config --global --add safe.directory "*";如何使用git工具初始胡项目并且和远程仓库建立联系 git init–建立一个本地仓库 git add README.md–将README.md文件加入…

【技术升级】Docker环境下Nacos平滑升级攻略,安全配置一步到位

目前项目当中使用的Nacos版本为2.0.2&#xff0c;该版本可能存在一定的安全风险。软件的安全性是一个持续关注的问题&#xff0c;尤其是对于像Nacos这样的服务发现与配置管理平台&#xff0c;它在微服务架构中扮演着核心角色。随着新版本的发布&#xff0c;开发团队会修复已知的…

代码签名证书的作用

代码签名证书&#xff08;Code Signing Certificate&#xff09;主要用于验证软件的完整性和开发者身份&#xff0c;确保用户在下载或安装软件时能够确认该软件未被篡改&#xff0c;并且确实来自于其所声称的发布者。以下是代码签名证书的主要作用&#xff1a; 验证软件来源&am…

Vue Promise 必须在外层,放到其它比如ElMessageBox,将不会返回任何值

当点击switch按钮之前&#xff0c;如果当更新后再刷新的效果不好&#xff0c;需要判断行为&#xff0c;然后再决定是否打开按钮。 正确如下&#xff1a; return new Promise((resolve,reject) > {ElMessageBox.confirm(Hold?, Warning, {confirmButtonText: Yes,cancelButt…

优秀的Linux Shell终端Starship Shell的安装和配置

文章目录 简介安装startship1.安装 starship 二进制文件:2.将初始化脚本添加到您的 shell 的配置文件3、配置4、日志安装字体nerd-fonts编写脚本安装字体Nerd字体全量安装文档简介 Starship是一款轻量、迅速、可无限定制的高颜值终端! Starship Shell是一个用Rust编写的开源…

虚拟机迁移报错:虚拟机版本与主机“x.x.x.x”的版本不兼容

1.虚拟机在VCenter上从一个ESXi迁移到另一个ESXi上时报错&#xff1a;虚拟机版本与主机“x.x.x.x”的版本不兼容。 2.例如从10.0.128.13的ESXi上迁移到10.0.128.11的ESXi上。点击10.0.128.10上的任意一台虚拟机&#xff0c;查看虚拟机版本。 3.确认要迁移的虚拟机磁盘所在位…

怎么理解FPGA的查找表与CPLD的乘积项

怎么理解 fpga的查找表 与cpld的乘积项 FPGA&#xff08;现场可编程门阵列&#xff09;和CPLD&#xff08;复杂可编程逻辑器件&#xff09;是两种常见的数字逻辑器件&#xff0c;它们在内部架构和工作原理上有着一些显著的区别。理解FPGA的查找表&#xff08;LUT&#xff0c;L…

系统RDSCPU打满问题分析报告

作者&#xff1a;琉璃 1. 问题概述 在2023年9月01日09点13分&#xff0c;玳数运维组侧接收到业务侧反馈系统响应缓慢&#xff0c;与此同时运维群内新系统RDS 发出CPU打满的告警&#xff0c;告警通知如下&#xff1a; 2. 问题分析 a. 数据库会话管理核查 玳数运维组侧登录…

动态规划之三—— 从暴力递归到动态规划_数字字符串转字母字符串

题目&#xff1a; 规定1 和A 对应&#xff0c;2 和B对应&#xff0c;3 和C 对应 ... 那么一个数字字符串&#xff0c;比如“111” 就可以转化为&#xff1a;“AAA” 、“KA”、“AK” 。要求&#xff1a;给定一个只有数字字符组成的字符串str&#xff0c; 返回有多少种转化结果…