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 提供了 ifswitch-case 两种语句形式;我们就先从 Go 语言分支结构之一的 if 语句开始讲起。

二、if 语句

2.1 if 语句介绍

if 语句是 Go 语言中提供的一种分支控制结构,它也是 Go 中最常用、最简单的分支控制结构。它会根据布尔表达式的值,在两个分支中选择一个执行。

2.2 单分支结构的 if 语句形式

单分支结构的if语句包含一个条件表达式和一个要执行的代码块。如果条件表达式的值为true,则执行代码块。如果条件表达式的值为false,则代码块将被跳过。以下是单分支结构的if语句的一般形式:

if boolean_expression {// 新分支
}// 原分支

这个 if 语句中的代码执行流程就等价于下面这幅流程图:

  • boolean_expression是一个布尔表达式,通常返回truefalse
  • 如果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:当ab都为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 分支中的代码析出到一个函数中,再对原函数实施“快乐路径”原则。

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

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

相关文章

Linux:redis的基础操作

redis介绍&#xff0c;安装和性能测试 Linux&#xff1a;redis数据库源码包安装-CSDN博客https://blog.csdn.net/w14768855/article/details/133752744?spm1001.2014.3001.5501如果没有了解过redis那么一定要去看看介绍 登录 redis-cli 可以登录到本机127.0.0.1&#xff0c;…

C++学习——静态成员变量、静态成员函数

以下内容源于C语言中文网的学习与整理&#xff0c;非原创&#xff0c;如有侵权请告知删除。 一、静态成员变量详解 1、被static修饰 1不同的对象占用不同的内存&#xff0c;这使得不同对象的成员变量相互独立&#xff0c;因此它们的值不受其他对象的影响。例如有两个相同类型…

没用的知识增加了,尝试用文心实现褒义词贬义词快速分类

尝试用文心实现褒义词贬义词快速分类 一、我的需求二、项目环境搭建千帆SDK安装及使用流程 三、项目实现过程创建应用获取签名调用接口计算向量积总结 百度世界大会将于10月17日在北京首钢园举办&#xff0c;今天进入倒计时五天了。通过官方渠道的信息了解到&#xff0c;这次是…

Jmeter连接mysql数据库详细步骤

一、一般平常工作中使用jmeter 连接数据库的作用 主要包括&#xff1a; 1、本身对数据库进行测试&#xff08;功能、性能测试&#xff09;时会需要使用jmeter连接数据库 2、功能测试时&#xff0c;测试出来的结果需要和数据库中的数据进行对比是否正确一致。这时候可以通过j…

XML外部实体注入攻击XXE

xml是扩展性标记语言&#xff0c;来标记数据、定义数据类型&#xff0c;是一种允许用户对自己的标记语言进行定义的源语言。XML文档结构包括XML声明、DTD文档类型定义&#xff08;可选&#xff09;、文档元素&#xff0c;一般无法直接打开&#xff0c;可以选择用excl或记事本打…

内存空间的分配与回收之连续分配管理方式

1.连续分配管理方式 连续分配:指为用户进程分配的必须是一个连续的内存空间。 1.单一连续分配 在单一连续分配方式中&#xff0c;内存被分为系统区和用户区。系统区通常位于内存的低地址部分&#xff0c;用于存放操作系统相关数据;用户区用于存放用户进程相关数据。内存中只…

BES耳机空间音频技术实现

BES耳机空间音频技术实现 是否需要申请加入数字音频系统研究开发交流答疑群(课题组)?加我微信hezkz17, 本群提供音频技术答疑服务 音响和耳机在空间音频技术上实现方式是不同的 虚拟现实可谓是空间音频技术最具代表性的应 用领域。虽然虚拟现实的起源可以追溯到1 9 6 8年, …

2023年淘宝天猫双11活动时间什么时候开始到几月几号结束?

2023年淘宝天猫双11超级红包领取时间 第一阶段&#xff1a;2023年10月24日20:00 至11月03日23:59 第二阶段&#xff1a;2023年11月04日00:00 至 11月11日23:59 2023年淘宝天猫双11超级红包使用时间 第一阶段&#xff1a;2023年10月31日20:00 至11月03日23:59 第二阶段&…

字符串左旋 与 字符串旋转结果

字符串左旋 实现一个函数&#xff0c;可以左旋字符串中的k个字符。 例如&#xff1a; ABCD左旋一个字符得到BCDA ABCD左旋两个字符得到CDAB 方法1 三步翻转法 要求:abcdef 左旋两个 整体逆序:fedcba左边逆序:cdef ba右边逆序:cdef ab #include<stdio.h> #include<…

Xcode 14.3.1build 报错整理

1、Command PhaseScriptExecution failed with a nonzero exit code 2、In /Users/XX/XX/XX/fayuan-mediator-app-rn/ios/Pods/CocoaLibEvent/lib/libevent.a(buffer.o), building for iOS Simulator, but linking in object file built for iOS, file /Users/XX/XX/XX/fayuan…

微服务设计原则:构建弹性和可维护的应用

文章目录 1. 单一职责原则2. 独立性和自治性3. 弹性和容错性4. API 网关5. 日志和监控6. 版本管理7. 自动化部署和持续集成8. 安全性9. 数据一致性10. 文档和通信拓展思考结论 &#x1f389;欢迎来到架构设计专栏~微服务设计原则&#xff1a;构建弹性和可维护的应用 ☆* o(≧▽…

Qt打开ui文件经常报错

报错如下&#xff1a; 解决方法&#xff1a; 最后设置成默认值 即可

竞赛选题 深度学习 机器视觉 人脸识别系统 - opencv python

文章目录 0 前言1 机器学习-人脸识别过程人脸检测人脸对其人脸特征向量化人脸识别 2 深度学习-人脸识别过程人脸检测人脸识别Metric Larning 3 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 深度学习 机器视觉 人脸识别系统 该项目…

高并发下的服务容错

在微服务架构中&#xff0c;我们将业务拆分成一个个的服务&#xff0c;服务与服务之间可以相互调用&#xff0c;但是由于网络 原因或者自身的原因&#xff0c;服务并不能保证服务的100%可用&#xff0c;如果单个服务出现问题&#xff0c;调用这个服务就会 出现网络延迟&#xf…

平面设计师之路 优漫动游

1、入门 平面设计师之路 首先将几个原则&#xff0c;不管你记得住记不住&#xff0c;这些方式和态度决定了你能够深入到平面设计的哪一步。 原则一&#xff1a;实践是掌握真理的唯一途径。这句话是衍生物&#xff0c;因为我觉得原来的那句话实验性质太浓厚了&#xff0c…

计算机网络传输层知识总结·

传输层提供的服务 传输层的功能 ●传输层提供进程之间的逻辑通信&#xff0c;即端到端的通信 ●复用和分用 ●差错检测&#xff08;首部和数据部分&#xff09; ●面向连接的TCP和无连接的UDP 端口的作用 ●端口标识的是主机中的进程 ●硬件端口是不同…

3D WEB轻量化引擎HOOPS:促进CAD软件的创新与协作

CAD软件一直以来都在现代工程、建筑、制造和设计领域发挥着至关重要的作用。在数字时代&#xff0c;CAD软件的开发者不断追求提高软件性能、增加功能和改善用户体验&#xff0c;在这一努力中&#xff0c;HOOPS技术&#xff08;高度优化的面向对象并行软件&#xff09;滑块露头角…

IDEA启动报错Failed to create JVM. JVM path的解决办法

今天启动IDEA时IDEA报错&#xff0c;提示如下。 if you already hava a JDK installed, define a JAVA_HOME variable in Computer > Systen Properties > System Settings > Environment Variables.Failed to create JVM. JVM path:D:\ideaIU2023.2.3\IntelliJ IDE…

使用c++视觉处理----canny 边缘检测、sobel边缘检测、scharr 滤波边缘检测

使用c视觉处理canny 边缘检测、sobel边缘检测、scharr 滤波边缘检测 #include <opencv2/opencv.hpp>int main() {// 读取图像cv::Mat image cv::imread("1.jpg", cv::IMREAD_GRAYSCALE); // 转为灰度图像if (image.empty()) {std::cerr << "无法加…

Spring Boot 中常用的注解@RequestParam

Spring Boot 中常用的注解RequestParam RequestParam 是 Spring Framework 和 Spring Boot 中常用的注解之一&#xff0c;用于从请求中获取参数值。它通常用于处理 HTTP 请求中的查询参数&#xff08;query parameters&#xff09;或表单数据。下面详细解释 RequestParam 的用…