Go If流程控制与快乐路径原则
Go if流程控制与快乐路径原则
文章目录
- Go if流程控制与快乐路径原则
- 一、流程控制基本介绍
- 二、if 语句
- 2.1 if 语句介绍
- 2.2 单分支结构的 if 语句形式
- 2.3 Go 的 if 语句的特点
- 2.3.1 分支代码块左大括号与if同行
- 2.3.2 条件表达式不需要括号
- 三、操作符
- 3.1 逻辑操作符
- 3.2 操作符的优先级
- 三、if 多(N)分支结构
- 3.1 if else(分支结构)
- 3.2 if(N)分支结构(if ... else if ... else)
- 四、if 语句的自用变量
- 五、if 语句的“快乐路径”原则
一、流程控制基本介绍
流程控制是每种编程语言控制逻辑走向和执行次序的重要部分,流程控制可以说是一门语言的“经脉”。
那么 Go 语言对分支与循环两种控制结构的支持是怎么样的呢?针对程序的分支结构,Go 提供了 if
和 switch-case
两种语句形式;我们就先从 Go 语言分支结构之一的 if 语句开始讲起。
二、if 语句
2.1 if 语句介绍
if 语句是 Go 语言中提供的一种分支控制结构,它也是 Go 中最常用、最简单的分支控制结构。它会根据布尔表达式的值,在两个分支中选择一个执行。
2.2 单分支结构的 if 语句形式
单分支结构的if
语句包含一个条件表达式和一个要执行的代码块。如果条件表达式的值为true
,则执行代码块。如果条件表达式的值为false
,则代码块将被跳过。以下是单分支结构的if
语句的一般形式:
if boolean_expression {// 新分支
}// 原分支
这个 if 语句中的代码执行流程就等价于下面这幅流程图:
boolean_expression
是一个布尔表达式,通常返回true
或false
。- 如果
boolean_expression
的值为true
,则执行// 当条件为真时执行的代码
部分的代码块。 - 如果
boolean_expression
的值为false
,则代码块将被跳过,继续执行下一个语句。
2.3 Go 的 if 语句的特点
2.3.1 分支代码块左大括号与if同行
if
语句的分支代码块的左大括号与 if
关键字在同一行上,这是 Go
代码风格的统一要求,gofmt
工具会帮助我们实现这一点;
2.3.2 条件表达式不需要括号
if 语句的布尔表达式整体不需要用括号包裹,这使得代码更加简洁。而且,if 关键字后面的条件判断表达式的求值结果必须是布尔类型,即要么是 true
,要么是 false
:
if runtime.GOOS == "darwin" {println("we are on MacOS")
}
如果判断的条件比较多,我们可以用多个逻辑操作符连接起多个条件判断表达式,比如这段代码就是用了多个逻辑操作符 && 来连接多个布尔表达式:
if (runtime.GOOS == "darwin") && (runtime.GOARCH == "amd64") &&(runtime.Compiler != "gccgo") {println("we are using standard go compiler on Mac os for amd64")}
上面示例代码中的每个布尔表达式都被小括号括上了,这是为了降低你在阅读和理解这段代码时,面对操作符优先级的心智负担。
三、操作符
3.1 逻辑操作符
逻辑操作符除了上面的 && 之外,Go 还提供了另外两个逻辑操作符,如下表:
逻辑操作符 | 含义 | 表达式求值举例 |
---|---|---|
&& | 逻辑与 | a &&b :当a 和b 都为true 时,该表达式的求值 结果为true |
` | ` | |
` | ` | 逻辑非 |
3.2 操作符的优先级
一元操作符,比如上面的逻辑非操作符,具有最高优先级,其他操作符的优先级如下:
优先级(从高到低) | 操作符列表 |
---|---|
5 | *, /, %, <<, >>, &, &^ |
4 | +, - |
3 | !=, ==, <, <=, >, >= |
2 | && |
1 | || |
- 优先级5的是乘、除、取模和位操作符
- 优先级4的是加法和减法运算符
- 优先级3的是关系和相等运算符
- 优先级2的是逻辑与
- 优先级最低的是逻辑或
操作符优先级决定了操作数优先参与哪个操作符的求值运算,我们以下面代码中 if 语句的布尔表达式为例:
func main() {a, b := false,trueif a && b != true {println("(a && b) != true")return}println("a && (b != true) == false")
}
这段代码会输出得到的是 a && (b != true) == false
。这是为什么呢?
这段代码的关键就在于,if 后面的布尔表达式中的操作数 b 是先参与 && 的求值运算,还是先参与!= 的求值运算。根据前面的操作符优先级表,我们知道,!= 的优先级要高于 &&,因此操作数 b 先参与的是!=
的求值运算,这样 if
后的布尔表达式就等价于 a && (b != true)
。
针对以上问题,推荐在 if 布尔表达式中,使用带有小括号的子布尔表达式来清晰地表达判断条件。
这样做不仅可以消除了自己记住操作符优先级的学习负担,当其他人阅读你的代码时,也可以很清晰地看出布尔表达式要表达的逻辑关系,这能让我们代码的可读性更好,更易于理解,不会因记错操作符优先级顺序而产生错误的理解。
三、if 多(N)分支结构
3.1 if else(分支结构)
Go语言中if else(分支结构)
条件判断的格式如下:
if boolean_expression {// 分支1
} else {// 分支2
}
3.2 if(N)分支结构(if … else if … else)
if
条件(N)分支结构格式如下:
if boolean_expression1 {// 分支1
} else if boolean_expression2 {// 分支2... ...} else if boolean_expressionN {// 分支N
} else {// 分支N+1
}
以下面是一个四分支的wei伪代码示例:
if boolean_expression1 {// 分支1
} else if boolean_expression2 {// 分支2
} else if boolean_expression3 {// 分支3
} else {// 分支4
}
以下是一个示例,演示如何使用if-else
结构来判断一个分数的等级:
package mainimport "fmt"func main() {score := 85if score >= 90 {fmt.Println("A")} else if score >= 80 {fmt.Println("B")} else if score >= 70 {fmt.Println("C")} else {fmt.Println("D")}
}
四、if 语句的自用变量
无论是单分支、二分支还是多分支结构,我们都可以在 if 后的布尔表达式前,进行一些变量的声明,在 if 布尔表达式前声明的变量,叫 if 语句的自用变量。顾名思义,这些变量只可以在 if 语句的代码块范围内使用,比如下面代码中的变量 a、b 和 c:
func main() {if a, c := f(), h(); a > 0 {println(a)} else if b := f(); b > 0 {println(a, b)} else {println(a, b, c)}
}
我们可以看到自用变量声明的位置是在每个 if 语句的后面,布尔表达式的前面,而且,由于声明本身是一个语句,所以我们需要把它和后面的布尔表达式通过分号分隔开。
在 if 语句中声明自用变量是 Go 语言的一个惯用法,这种使用方式直观上可以让开发者有一种代码行数减少的感觉,提高可读性。同时,由于这些变量是 if 语句自用变量,它的作用域仅限于 if 语句的各层隐式代码块中,if 语句外部无法访问和更改这些变量,这就让这些变量具有一定隔离性,这样你在阅读和理解 if 语句的代码时也可以更聚焦。
五、if 语句的“快乐路径”原则
上面我们已经学了 if 分支控制结构的三种形式了,从可读性上来看,单分支结构要优于二分支结构,二分支结构又优于多分支结构。那么显然,我们在日常编码中要减少多分支结构,甚至是二分支结构的使用,这会有助于我们编写出优雅、简洁、易读易维护且不易错的代码。
首先,我们来看一段伪代码段1:
//伪代码段1:func doSomething() error {if errorCondition1 {// some error logic... ...return err1}// some success logic... ...if errorCondition2 {// some error logic... ...return err2}// some success logic... ...return nil
}
我们看到单分支控制结构的伪代码段 1 有这几个特点:
- 没有使用 else 分支,失败就立即返回;
- “成功”逻辑始终“居左”并延续到函数结尾,没有被嵌入到 if 的布尔表达式为 true 的代码分支中;
- 整个代码段布局扁平,没有深度的缩进;
- 代码的可读性很高
我们来看一段伪代码段2:
// 伪代码段2:func doSomething() error {if successCondition1 {// some success logic... ...if successCondition2 {// some success logic... ...return nil} else {// some error logic... ...return err2}} else {// some error logic... ...return err1}
}
伪代码段 2 实现了同样逻辑码段 1,就使用了带有嵌套的二分支结构,它的特点如下:
- 整个代码段呈现为“锯齿状”,有深度缩进;
- “成功”逻辑被嵌入到
if
的布尔表达式为true
的代码分支中;
很明显,伪代码段 1 的逻辑更容易理解,也更简洁。Go 社区把这种 if 语句的使用方式称为 if 语句的“快乐路径(Happy Path)”原则,所谓“快乐路径”也就是成功逻辑的代码执行路径,它的特点是这样的:
-
仅使用单分支控制结构;
-
当布尔表达式求值为
false
时,也就是出现错误时,在单分支中快速返回; -
正常逻辑在代码布局上始终“靠左”,这样读者可以从上到下一眼看到该函数正常逻辑的全貌;
-
函数执行到最后一行代表一种成功状态。
Go 社区推荐 Gopher 们在使用 if 语句时尽量符合这些原则,如果你的函数实现代码不符合“快乐路径”原则,你可以按下面步骤进行重构:
-
尝试将“正常逻辑”提取出来,放到“快乐路径”中;
-
如果无法做到上一点,很可能是函数内的逻辑过于复杂,可以将深度缩进到 else 分支中的代码析出到一个函数中,再对原函数实施“快乐路径”原则。
相关文章:

Go If流程控制与快乐路径原则
Go if流程控制与快乐路径原则 文章目录 Go if流程控制与快乐路径原则一、流程控制基本介绍二、if 语句2.1 if 语句介绍2.2 单分支结构的 if 语句形式2.3 Go 的 if 语句的特点2.3.1 分支代码块左大括号与if同行2.3.2 条件表达式不需要括号 三、操作符3.1 逻辑操作符3.2 操作符的…...

yolov8 strongSORT多目标跟踪工具箱BOXMOT
1 引言 多目标跟踪MOT项目在Github中比较完整有:BOXMOT , 由mikel brostrom提供。在以前的版本中,有yolov5deepsort(版本v3-v5), yolov8strongsort(版本v6-v9),直至演变…...

如何开发一款跑酷游戏?
跑酷游戏(Parkour Game)是一种流行的视频游戏类型,玩家需要在游戏中控制角色进行极限动作、跳跃、爬墙和各种动作,以完成各种挑战和任务。如果你有兴趣开发一款跑酷游戏,以下是一些关键步骤和考虑事项: 游…...

使用宝塔面板在Linux上搭建网站,并通过内网穿透实现公网访问
文章目录 前言1. 环境安装2. 安装cpolar内网穿透3. 内网穿透4. 固定http地址5. 配置二级子域名6. 创建一个测试页面 前言 宝塔面板作为简单好用的服务器运维管理面板,它支持Linux/Windows系统,我们可用它来一键配置LAMP/LNMP环境、网站、数据库、FTP等&…...

Unity可视化Shader工具ASE介绍——6、通过例子说明ASE节点的连接方式
大家好,我是阿赵。继续介绍Unity可视化Shader编辑插件ASE的用法。上一篇已经介绍了很多ASE常用的节点。这一篇通过几个小例子,来看看这些节点是怎样连接使用的。 这篇的内容可能会比较长,最终是做了一个遮挡X光的效果,不过把这…...

VUE3基础知识梳理
VUE3基础知识梳理 一、vue了解和环境搭建1.vue是什么:cn.vuejs.org/vuejs.org2.渐进式框架3.vue的版本4.vueAPI的风格5.准备环境5.1.创建vue项目5.2.vue的目录结构 二、vue3语法1.干净的vue项目2.模板语法2.1 文本插值2.2属性绑定2.3条件渲染2.4列表渲染2.5通过key管…...

Java架构师缓存通用设计方案
目录 1 采用多级缓存2 缓存数据尽量前移3 静态化4 数据平衡策略5 jvm缓存的问题6 redis存放数据解决7 redis垂直拆分8 总结1 采用多级缓存 在实际应用中需要考虑的实际问题。首先,前端页面可以做缓存,虽然图上没有显示,但在现实应用中这是提高性能的一个重要方面。前端页面缓…...

2023年【危险化学品生产单位安全生产管理人员】及危险化学品生产单位安全生产管理人员模拟考试题
题库来源:安全生产模拟考试一点通公众号小程序 危险化学品生产单位安全生产管理人员考前必练!安全生产模拟考试一点通每个月更新危险化学品生产单位安全生产管理人员模拟考试题题目及答案!多做几遍,其实通过危险化学品生产单位安…...

微信小程序 在bindscroll事件中监听scroll-view滚动到底
scroll-view其实提供了一个 bindscrolltolower 事件 这个事件的作用是直接监听scroll-view滚动到底部 但是 总有不太一样的情况 公司的项目 scroll-view 内部 最下面有一个 类名叫 bottombj 的元素 我希望 滚动到这个 bottombj 上面的时候就开始加载滚动分页 简单说 bottombj这…...

收银系统商品定价设计思考
一、背景 因为门店系统里商品总共也就几万款,一直以来都是根据条码由总部统一定价销售,现在有加盟店,各门店也有进行各自促销活动的需求,这就需要放开门店自主定价权,所以近段时间系统在商品定价上做了扩展。 二、商…...
Kotlin函数作为参数指向不同逻辑
Kotlin函数作为参数指向不同逻辑 fun sum(): (Int, Int) -> Int {return { a, b -> (a b) } }fun multiplication(): (Int, Int) -> Int {return { a, b -> (a * b) } }fun main(args: Array<String>) {var math: (Int, Int) -> Intmath sum()println(m…...

读书笔记—《如何阅读一本书》
读书笔记—《如何阅读一本书》 一、阅读的层次1、主动阅读的基础一个阅读者要提出的四个基本问题 2、基础阅读(第一层)3、检视阅读(第二层)4、分析阅读(第三层) 二、阅读不同读物的方法三、阅读的最终目标1…...

Kafka数据同步原理详解
Kafka数据同步原理详解 Kafka是一种分布式的消息队列系统,它具有高吞吐量、可扩展性和分布式特性等优势。在Kafka中,数据按照主题进行分区,每个主题都有一组分区。每个分区都有自己的生产者和消费者,生产者负责向分区中写入消息&…...

C++课程总复习
一、c的第一条程序 1.cout cout >输出类对象,用来输出的,可以自动识别类型,所以不需要加格式符号 << 插入符(输出符号) endl 换行>\n #include <iostream> //#预处理 //include 包含 相应的头…...

数据结构—顺序表
目录 1.线性表 2.顺序表概念 3.实现顺序表 (1)声明结构体 (2)初始化 (3)打印数据 (4) 销毁 (5)尾插&头插 尾插 判断是否扩容 头插 (6)尾删&头删 尾删 头删 (7)指定位置插入元素 (8)删除指定位置元素 (9)查找指定元素位置 (10)修改指定位置元素 完整版…...

企业服务器租用对性能有什么要求呢?
企业租用服务器租用首要的是稳定,其次是安全,稳定是为了让企业的工作能够顺利进行,只有性能稳定的服务器才能保证网站之类的正常工作,就让小编带大家看一看有什么要求吧! 服务器简单介绍。服务器是在网络上为其它客户机…...

2731.移动机器人
2731. 移动机器人 - 力扣(LeetCode) 有一些机器人分布在一条无限长的数轴上,他们初始坐标用一个下标从 0 开始的整数数组 nums 表示。当你给机器人下达命令时,它们以每秒钟一单位的速度开始移动。 给你一个字符串 s ,…...

相交链表Java
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 nu11。 以下有两种解决方法: 一种是用Map,利用其key值唯一的方法去判断(也可以使用set,set在add时,已存在的元素会返回false,不存在的返回…...

第二章:OSI参考模型与TCP/IP模型
OSI参考模型与TCP/IP模型 一、OSI参考模型二、TCP/IP模型2.1 四层分法(书上)2.2 五层分法(实际厂商)2.3 数据封装和解封装2.3.1 封装2.3.2 解封装2.3.3 TCP/IP分层封装2.3.4 数据封装和解封装过程 一、OSI参考模型 1.物理层 定义电…...

知识图谱04——openGL与ubuntu22.04
跑图神经网络的时候遇到了如下问题 libGL error: failed to load driver: iris libGL error: MESA-LOADER: failed to open iris: /usr/lib/dri/iris_dri.so: 无法打开共享对象文件: 没有那个文件或目录 (search paths /usr/lib/x86_64-linux-gnu/dri:\$${ORIGIN}/dri:/usr/li…...

如何看待为了省小钱而花费时间
相信每个人都会遇到这种情况:购买东西时想着货比三家或者想办法领优惠券、凑单等就可以省下一些钱,但是需要花费不少时间和精力。这时就开始犹豫了:省钱是必要的,需要居安思危,等到缺钱的时候不会后悔;又想…...

Maven Eclipse
Eclipse 提供了一个很好的插件 m2eclipse ,该插件能将 Maven 和 Eclipse 集成在一起。 在最新的 Eclipse 中自带了 Maven,我们打开,Windows->Preferences,如果会出现下面的画面: 下面列出 m2eclipse 的一些特点&a…...

Linux:redis集群(3.*版本 和 5.*版本)搭建方法
介绍 至少6个实例才能组成集群。3主3从会自动分配 Redis集群原理 Redis集群架构 Redis Cluster采用虚拟槽分区,将所有的数据根据算法映射到0~16383整数槽内 Redis Cluster是一个无中心的结构 每个节点都保存数据和整个集群的状态 集群角色 Master:Master…...

正则表达式基础语法
https://tool.oschina.net/regex 正则表达式:检查、匹配字符串的表达式 单个字符匹配: 有特殊含义的匹配: 多次重复匹配: 限定开头结尾的匹配: 贪婪模式:在满足条件的情况下,尽可能多匹配…...

数据库常见面试题--MySQL
梳理面试过程中数据库相关的常见问题,需要说明的是,这篇文章主要是基于MySQL数据库,其他类型的数据库还请自行参考使用。 数据库概述 为什么使用数据库 1、数据库增删改查更方便 2、提供了事务的能力 本质是更好的管理数据。 数据库体系结…...

Springboot 集成 Redis集群配置公网IP连接报私网IP连接失败问题
1、问题:在Springboot 集成 Redis集群配置公网IP连接报私网IP连接失败,一直报私有IP连接失败 14 14:57:49.180 WARN 22012 --- [ioEventLoop-6-4] i.l.c.c.topology.ClusterTopologyRefresh : Unable to connect to [192.168.0.19:6384]: connection …...

解决方案 | 法大大电子签精准击破销售场景签约难题
新商业形态及新交易模式不断涌现,电子签已经成为现代商业活动中不可或缺的一部分。特别是在销售场景中,电子签的应用不仅可以提高销售效率,还可以降低成本,提高客户满意度。本文将详细分析电子签在销售场景中的应用价值能力&#…...

ARM按键中断控制事件
设置按键中断,按键1按下,LED亮,再按一次,灭按键2按下,蜂鸣器响。再按一次,不响按键3按下,风扇转,再按一次,风扇停 src/key_it.c #include"key_it.h" //GPIO初…...

微信小程序之本地生活(九宫格)
文章目录 一.创建项目二.配置修改json三.编写WXML四.编写WXSS五.最终效果 一.创建项目 创建新的项目,名称为:本地生活 二.配置修改json 在app.json中删除其他页面 将index改为grid 自动生成新的文件 添加自己的轮播图片 源代码: <!--…...

【Linux 安装Kibana 及 Es 分词器安装】
一、客户端Kibana安装 Kibana是一个开源分析和可视化平台,旨在与Elasticsearch协同工作。参考文档 1. 下载并解压缩Kibana 下载路径 选择的版本是和 ElasticSearch 对应(7.17.3) 下载后上传到Linux 系统中,并放在 /root/ 下&a…...