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.hqwc.cn/news/133019.html

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

相关文章

Halcon我的基础教程(一)(我的菜鸟教程笔记)-halcon仿射变换(Affine Transformation)的探究与学习

目录 什么是仿射变换?仿射变换有哪些方式?任何仿射变换都能由以下基本变换构造而来:在Halocn中,仿射变换具有重要的作用,那我们本文章重点讨论仿射变换基础性知识。 使用Halcon中的重要算子,仿射变换一般解决步骤,案例应用会在以后的文章中我们重点解答与讨论。 我们首先…

从0开始学Java:Java概述

文章目录 1. JavaSE体系介绍2. 计算机语言介绍3. Java语言概述3.1 Java生态圈3.2 Java语言发展历史3.3 Java技术体系平台3.4 Java的主要应用领域3.5 Java语言的特点 4. Java语言跨平台原理 1. JavaSE体系介绍 JavaSE知识模块介绍 第一部分&#xff1a;计算机编程语言核心结构&…

微信小程序wxml使用过滤器

微信小程序wxml使用过滤器 1. 新建wxs2. 引用和使用 如何在微信小程序wxml使用过滤器&#xff1f; 犹如Angular使用pipe管道这样子方便&#xff0c;用的最多就是时间格式化。 下面是实现时间格式化的方法和步骤&#xff1a; 1. 新建wxs 插入代码&#xff1a; /*** 管道过滤工…

docker应用记录总结

一、前言 docker这类部署工具&#xff0c;久而久之不使用非常容易忘记&#xff0c;甚至连操作命令都容易忘记。网上也有比较全的docker使用教程。这里做一个记录总结&#xff0c;纯属是温故知新。 二、docker部署应用 1、docker印象 docker首先让我想到的是是虚拟化技术&…

Docker-compos

Docker-compose 简介 Docker-Compose项目是基于Python开发的Docker官方开源项目&#xff0c;负责实现对Docker容器集群的快速编排。 Docker-Compose将所管理的容器分为三层&#xff0c;分别是 工程&#xff08;project&#xff09;&#xff0c;服务&#xff08;service&#…

telnet 连接闪退

目录 问题查找问题解决参考 问题 在使用 telnet 连接板子时&#xff0c;出现了 telnet 界面一闪而逝的情况。正常板子开机&#xff0c;查看其存在 telnetd 进程&#xff0c;而使用 telnet 后&#xff0c;登录界面一闪而逝&#xff0c;板子上的 telnetd 进程消失。 查找问题 …

基于SSM的网络安全宣传网站设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

AndroidStudio模拟器,没有Google Play的就有ROOT权限

正确选择版本 测试 D:\>adb shell emulator64_x86_64:/ $ su emulator64_x86_64:/ #

多标签分类论文笔记 | ML-Decoder: Scalable and Versatile Classification Head

个人论文精读笔记&#xff0c;主要是翻译心得&#xff0c;欢迎旁观&#xff0c;如果有兴趣可以在评论区留言&#xff0c;我们一起探讨。 Paper: https://arxiv.org/pdf/2111.12933.pdf Code: https://github.com/Alibaba-MIIL/ML_Decoder 文章目录 0. 摘要1. 介绍2. 方法2.1 Ba…

高效团队协作软件推荐:提升工作效率的优选方案!

使用团队协作软件有什么好处&#xff1f;可以摆脱过时的电子表格&#xff0c;有了单一的真实来源&#xff0c;您可以随时检查任何任务并获得可用的最新信息。 一目了然地查看所有正在进行的工作&#xff0c;看板式面板、甘特图和燃尽图等可视化工具可让您随时轻松获得项目的高级…

OCR让点读笔如虎添翼

点读笔是一种智能学习工具&#xff0c;它可以通过识别文字来提供相应的语音或图像反馈。在实现文字识别功能时&#xff0c;点读笔通常会借助OCR&#xff08;Optical Character Recognition&#xff0c;光学字符识别&#xff09;技术。下面将详细介绍点读笔如何利用OCR技术实现文…

SLAM从入门到精通(launch文件学习)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 大家应该还记得我们在一开始学习ros的时候&#xff0c;如果需要启动一个节点的话&#xff0c;需要首先打开roscore&#xff0c;接着用rosrun打开对…