Tony Bai · Go 语言第一课
Tony Bai
资深架构师,tonybai.com 博主
21492 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 59 讲
开篇词 (1讲)
结束语 (1讲)
Tony Bai · Go 语言第一课
15
15
1.0x
00:00/00:00
登录|注册

18|控制结构:if的“快乐路径”原则

等价变换
推荐使用方式
优势
优点与不足
作用域
多分支结构
二分支结构
操作符优先级
逻辑操作符
布尔表达式不需要括号
代码风格要求
代码执行流程
布尔表达式
“快乐路径”原则
自用变量
其他形式
Go语言特点
基本形式
for循环
if和switch-case
if语句
Go语言支持的控制结构
思考题
分支和循环
程序=数据结构+算法
Go控制结构

该思维导图由 AI 生成,仅供参考

你好,我是 Tony Bai。
1984 年图灵奖获得者、著名计算机科学家尼古拉斯·沃斯(Niklaus Wirth)提出过著名的“程序 = 数据结构 + 算法”的公式。在前面的课程中,我们花了很多时间讲解了 Go 语言的基本数据类型和复合数据类型,这些对应的就是公式中数据结构,通过这些数据类型我们可以建立起复杂的数据结构。
那么公式中的算法呢?算法是对真实世界运作规律的抽象,是解决真实世界中问题的步骤。在计算机世界中,再复杂的算法都可以通过顺序、分支和循环这三种基本的控制结构构造出来。
顺序结构自然不用说了,我们要关注的主要是后面两个。所以,这一节课开始的连续三节课,我们都会聚焦于 Go 语言中的分支和循环这两种控制结构。
那么 Go 语言对分支与循环两种控制结构的支持是怎么样的呢?针对程序的分支结构,Go 提供了 if 和 switch-case 两种语句形式;而针对循环结构,Go 只保留了 for 这一种循环语句形式。这节课我们就先从 Go 语言分支结构之一的 if 语句开始讲起。

Go 中的分支结构之认识 if 语句

01 讲中我们提到过,Go 语言是站在 C 语言等的肩膀之上诞生与成长起来的。Go 语言继承了 C 语言的很多语法,这里就包括控制结构。但 Go 也不是全盘照搬,而是在继承的基础上又加上了自己的一些优化与改进,比如:
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

Go语言中的if语句是一种常用的分支控制结构,具有一些特点:代码块的左大括号与if关键字在同一行,布尔表达式不需要用括号包裹,且必须是布尔类型的值。文章介绍了多种形式的if语句,包括二分支结构和多分支结构。在多分支结构中,可以通过等价变换将其转换为层层缩进的二分支结构,便于理解和阅读。此外,文章还提到了操作符优先级的概念,并建议在布尔表达式中使用带有小括号的子布尔表达式,以提高代码的可读性和理解性。另外,文章介绍了if语句的“快乐路径”原则,即尽量使用单分支控制结构,使代码更易读、简洁、易维护。总的来说,通过本文的介绍,读者可以快速了解Go语言中if语句的特点和使用方法。文章还提出了一个思考题,引发读者对if语句中布尔表达式排列的思考。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Tony Bai · Go 语言第一课》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(23)

  • 最新
  • 精选
  • Darren
    如果加上“布尔表达式3在这段代码中实际被命中的机会更多,布尔表达式2次之,布尔表达式1最少”,这个条件,那么最优的性能最好的写法应该是最大概率的放到最前面,因此可以改成如下: func foo() { if boolean_expression3{ return } if boolean_expression2 { return } if boolean_expression1 { return } else代码 return } 那为什么命中的最多,写到前面,是最好的呢,这里面主要涉及到2个技术点:流水线技术和分支预测 流水线技术:简单的说,一条 CPU 指令的执行是由 取指令-指令译码-指令执行-结果回写组成的(简单的说哈,真实的流水线是更长更复杂的);第一条指令译码的时候,就可以去取第二条指令,因此可以通过流水线技术提高CPU的使用率。 分支预测:如果没有任何分支预测,那么就是按照程序的代码顺序执行,那么执行到if上一句的时候,指令译码就是if语句,取指令就是if语句块的第一句,那么if如果不满足的话,就会执行JMP指令,跳转到else,因此流水线中的取指令与指令译码其实是无用功。因此在没有任何分支预测优化的情况下,if语句需要把概率更高的条件写到最上面,更能体现流水线的威力。 但是现代计算机都有分支预测的优化,比如动态分支预测等技术,但是不管怎么说,把概率最大的放到最上面,还是很有必要的。 问题:在C语言中,有类似这样的宏定义,可以使用 __builtin_expect函数,主动提示那个分支的代码的概率更高,在go中是否也有类似的方法?还是说现在的编后端编译技术已经比较智能,不需要甚至禁止程序员手动指定分支预测了。 #define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0)

    作者回复: 厉害!👍

    2021-11-22
    12
    66
  • Darren
    可以改成这样子吧 func foo() { if boolean_expression1 { return } if boolean_expression2 { return } if boolean_expression3 { return } else代码 return }

    作者回复: 改成快乐路径方式是ok的。 可能思考题没说清楚。这里说的“效果最好”,指的是这段代码的执行性能最好。 提示一下:如果从统计概率而言,布尔表达式3在这段代码中实际被命中的机会更多,布尔表达式2次之,布尔表达式1最少,那么排列应该如何变 化才能让这段代码执行性能最好呢?

    2021-11-22
    2
    8
  • lesserror
    对于if的“快乐路径”原则深表认同,每次看见同事写的四五层以上的 if else 语句,有种“想死” 的冲动。 自以为逻辑能力很强,殊不知自己一时爽,后来者却无法维护这样的代码了。

    作者回复: 是的。尽量写简洁易读的代码才是王道。

    2021-11-23
    4
  • mikewoo
    依据被命中的概率,依次由高到低,把命中概率最高的放在最前面。

    作者回复: 👍

    2022-04-22
    3
  • Empty
    func foo() { if boolean_expression1 { return } if boolean_expression2 { return } if boolean_expression3 { return } else代码 return } 从代码的可读性来讲应该写成这个样子,但是多个if条件的排列顺序要综合命中概率、条件判断复杂度、业务优先级等方面去考虑

    作者回复: ✅

    2022-02-19
    3
  • 🐎
    这个其他语言里一般叫做 early return(尽早返回)😁

    作者回复: 👍

    2022-09-05归属地:北京
    3
    2
  • qinsi
    还是之前的问题,happy path似乎让comma ok不再简洁 comma ok: ```go m := map[string]int { "v1": 1, "v2": 2, "v3": 3, } if v1, ok := m["v1"]; ok { if v2, ok := m["v2"]; ok { if v3, ok := m["v3"]; ok { fmt.Println(v1 + v2 + v3) } } } ``` happy path: ```go v1, ok := m["v1"] if !ok { return } v2, ok := m["v2"] if !ok { return } v3, ok := m["v3"] if !ok { return } fmt.Println(v1 + v2 + v3) ``` 换种写法也一样: ```go if _, ok := m["v1"]; !ok { return } v1 := m["v1"] if _, ok := m["v2"]; !ok { return } v2 := m["v2"] if _, ok := m["v3"]; !ok { return } v3 := m["v3"] fmt.Println(v1 + v2 + v3) ```

    作者回复: 相对于第一个深层嵌套的“不易读”,下面虽然verbose一些,但代码一目了然啊。不知你是否有同样感觉。

    2021-11-22
    5
    2
  • 子杨
    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) } } 这个例子是不是有点问题,为啥 f() 第一个 if 里赋值给了 a,第二个又赋值给了b。

    作者回复: 这个只是为了演示“if语句的自用变量”的用法和作用域,不用太在意条件逻辑的合理性:)。

    2023-03-29归属地:广东
    1
  • Tristana
    假设每个表达式被命中概率为 boolean_expression1 > boolean_expression2 > boolean_expression3 , 可以将程序逻辑调整为命中率高的表达式放在最前面,命中后直接返回,调整后的逻辑如下 ``` func foo() { if boolean_expression1 { return 结果1 } if boolean_expression2 { return 结果2 } if boolean_expression3 { return 结果3 } return 结果5 } ```

    作者回复: ✅

    2022-06-10
    1
  • Forest
    使用switch case; func foo() { switch expression { case condition1: case condition2: case condition3: default: } }

    作者回复: 可能思考题没说清楚。这里说的“效果最好”,指的是这段代码的执行性能最好。 提示一下:如果从统计概率而言,布尔表达式3在这段代码中实际被命中的机会更多,布尔表达式2次之,布尔表达式1最少,那么排列应该如何变化才能让这段代码执行性能最好呢? btw,使用switch-case,说明你基础很好。

    2021-11-22
    1
收起评论
显示
设置
留言
23
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部