运维八一 运维八一
首页
运维杂记
编程浅尝
周积跬步
专栏
生活
关于
收藏
  • 分类
  • 标签
  • 归档
Source (opens new window)

运维八一

运维,运维!
首页
运维杂记
编程浅尝
周积跬步
专栏
生活
关于
收藏
  • 分类
  • 标签
  • 归档
Source (opens new window)
  • Go

    • 前言

    • Go基础知识

    • Go基本语法

    • 实战项目:简单web服务

    • 基本数据类型

    • 内置运算符

    • 分支和循环

      • if(分支结构)
        • 1. if(分支结构)
          • 1.1 if介绍
          • 1.2 操作符优先级
          • 1.3 if写法
          • 1.3.1 最简形式
          • 1.3.2 二分支结构
          • 1.3.3 多(N)分支结构
          • 1.3 支持声明 if 语句的自用变量
          • 1.4 if 语句的“快乐路径”原则
      • switch(分支结构)
      • for(循环结构)
      • break和continue语句
    • 函数 function

    • 结构体 struct

    • 方法 method

    • 实战项目:跟踪函数调用链

    • 接口 interface

    • 并发 concurrency

    • 指针

    • 实战项目:实现轻量级线程池

    • 实战项目:实现TCP服务器

    • go常用包

    • Gin框架

    • go随记

  • Python

  • Shell

  • Java

  • Vue

  • 前端

  • 编程浅尝
  • Go
  • 分支和循环
lyndon
2022-06-07
目录

if(分支结构)

1984 年图灵奖获得者、著名计算机科学家尼古拉斯·沃斯(Niklaus Wirth)提出过著名的**“程序 = 数据结构 + 算法”**的公式。

  • 数据结构:基本数据类型和复合数据类型;
  • 算法:对真实世界运作规律的抽象,是解决真实世界中问题的步骤。在计算机世界中,再复杂的算法都可以通过顺序、分支和循环这三种基本的控制结构构造出来。

针对程序的分支结构,Go 提供了 ==if 和 switch-case== 两种语句形式;

针对循环结构,Go 只保留了 ==for== 这一种循环语句形式。

# 1. if(分支结构)

# 1.1 if介绍

Go 语言是站在 C 语言等的肩膀之上诞生与成长起来的。Go 语言继承了 C 语言的很多语法,这里就包括控制结构。但 Go 也不是全盘照搬,而是在继承的基础上又加上了自己的一些优化与改进,比如:

  • Go 坚持“一件事情仅有一种做法的理念”,只保留了 for 这一种循环结构,去掉了 C 语言中的 while 和 do-while 循环结构;
  • Go 填平了 C 语言中 switch 分支结构中每个 case 语句都要以 break 收尾的“坑”;
  • Go 支持了 type switch 特性,让“类型”信息也可以作为分支选择的条件;
  • Go 的 switch 控制结构的 case 语句还支持表达式列表,让相同处理逻辑的多个分支可以合并为一个分支,等等。

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

if boolean_expression {
    // 新分支
}

// 原分支
1
2
3
4
5

**分支结构是传统结构化程序设计中的基础构件,**这个 if 语句中的代码执行流程就等价于下面这幅流程图:

img

Go 的 if 语句的特点:

  • 和 Go 函数一样,if 语句的分支代码块的左大括号与 if 关键字在同一行上,这也是 Go 代码风格的统一要求,gofmt 工具会帮助我们实现这一点;
  • if 语句的布尔表达式整体不需要用括号包裹,一定程度上减少了开发人员敲击键盘的次数。而且,if 关键字后面的条件判断表达式的求值结果必须是布尔类型,即要么是 true,要么是 false:
if runtime.GOOS == "linux" {
    println("we are on linux os")    
}
1
2
3

# 1.2 操作符优先级

如果判断的条件比较多,可以用多个逻辑操作符连接起多个条件判断表达式

if (runtime.GOOS == "linux") && (runtime.GOARCH == "amd64") &&
    (runtime.Compiler != "gccgo") {
    println("we are using standard go compiler on linux os for amd64")
}
1
2
3
4

逻辑运算符:

img

Go 语言的操作符是有优先级的。这里要记住,一元操作符,比如上面的逻辑非操作符,具有最高优先级,其他操作符的优先级如下:

img

操作符优先级决定了操作数优先参与哪个操作符的求值运算:

func main() {
    a, b := false,true
    if a && b != true {
        println("(a && b) != true")
        return
    }
    println("a && (b != true) == false")
}
1
2
3
4
5
6
7
8

这段代码的关键就在于,if 后面的布尔表达式中的操作数 b 是先参与 && 的求值运算,还是先参与!= 的求值运算。

根据前面的操作符优先级表,我们知道,!= 的优先级要高于 &&,因此操作数 b 先参与的是!= 的求值运算,这样 if 后的布尔表达式就等价于 a && (b != true) ,而不是我们最初认为的 (a && b) != true。

# 1.3 if写法

# 1.3.1 最简形式

// 当 boolean_expression 求值为 true 时,执行 新分支,否则,执行 原分支:
if boolean_expression {
    // 新分支
}

// 原分支
1
2
3
4
5
6

# 1.3.2 二分支结构

// 当 boolean_expression 求值为 true 时,执行分支 1,否则,执行分支 2:
if boolean_expression {
  // 分支1
} else {
  // 分支2
}
1
2
3
4
5
6

# 1.3.3 多(N)分支结构

if boolean_expression1 {
    // 分支1
} else if boolean_expression2 {
    // 分支2
} else if boolean_expression3 {
    // 分支3
} else {
    // 分支4
} 

// 变换成二分之结构 如下:

if boolean_expression1 {
    // 分支1
} else {
    if boolean_expression2 {
        // 分支2
    } else { 
        if boolean_expression3 {
            // 分支3
        } else {
            // 分支4
        } 
    }
}

// 这样等价转换后,我们得到一个层层缩进的二分支结构,通过上面我们对二分支的分析,再来理解这个结构就十分容易了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

其他例子:

package main

import "fmt"

func main() {
   score := 65
   if score >= 90 {
      fmt.Println("A")
   } else if score > 75 {
      fmt.Println("B")
   } else {
      fmt.Println("C")   // C
   }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 1.3 支持声明 if 语句的自用变量

无论是单分支、二分支还是多分支结构,都可以在 if 后的布尔表达式前,进行一些变量的声明,在 if 布尔表达式前声明的变量,我叫它 if 语句的自用变量。顾名思义,这些变量只可以在 if 语句的代码块范围内使用。

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)
    }
}
1
2
3
4
5
6
7
8
9

好处:

  • 直观上可以让开发者有一种代码行数减少的感觉,提高可读性;
  • 由于这些变量是 if 语句自用变量,它的作用域仅限于 if 语句的各层隐式代码块中,if 语句外部无法访问和更改这些变量,这就让这些变量具有一定隔离性,这样你在阅读和理解 if 语句的代码时也可以更聚焦。

缺点:

  • 注意“变量遮蔽”问题

在 if 表达式之前添加一个执行语句,再根据变量值进行判断。

package main

import "fmt"

func main() {
   // 这里的 score 是局部作用域
   if score := 65; score >= 90 {
      fmt.Println("A")
   } else if score > 75 {
      fmt.Println("B")
   } else {
      fmt.Println("C")   // C
      fmt.Println(score) // 65 只能在函数内部打印 score
   }
   //fmt.Println(score)   //undefined: score
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 1.4 if 语句的“快乐路径”原则

从可读性上来看,单分支结构要优于二分支结构,二分支结构又优于多分支结构。在日常编码中要减少多分支结构,甚至是二分支结构的使用,这会有助于我们编写出优雅、简洁、易读易维护且不易错的代码。

下面是两段逻辑相同但形式不同的伪代码段:

//伪代码段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
}

// 伪代码段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
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

代码段 1 特点:

  • 没有使用 else 分支,失败就立即返回;
  • “成功”逻辑始终“居左”并延续到函数结尾,没有被嵌入到 if 的布尔表达式为 true 的代码分支中;
  • 整个代码段布局扁平,没有深度的缩进;

伪代码段 2,使用了带有嵌套的二分支结构,它的特点如下:

  • 整个代码段呈现为“锯齿状”,有深度缩进;
  • “成功”逻辑被嵌入到 if 的布尔表达式为 true 的代码分支中;

很明显,伪代码段 1 的逻辑更容易理解,也更简洁。Go 社区把这种 if 语句的使用方式称为 if 语句的“快乐路径(Happy Path)”原则,所谓“快乐路径”也就是成功逻辑的代码执行路径,它的特点是这样的:

  • 仅使用单分支控制结构;
  • 当布尔表达式求值为 false 时,也就是出现错误时,在单分支中快速返回;
  • 正常逻辑在代码布局上始终“靠左”,这样读者可以从上到下一眼看到该函数正常逻辑的全貌;
  • 函数执行到最后一行代表一种成功状态。

Go 社区推荐 Gopher 们在使用 if 语句时尽量符合这些原则,如果你的函数实现代码不符合“快乐路径”原则,你可以按下面步骤进行重构:

  • 尝试将“正常逻辑”提取出来,放到“快乐路径”中;
  • 如果无法做到上一点,很可能是函数内的逻辑过于复杂,可以将深度缩进到 else 分支中的代码析出到一个函数中,再对原函数实施“快乐路径”原则。
上次更新: 2022/06/12, 15:48:09
赋值运算符
switch(分支结构)

← 赋值运算符 switch(分支结构)→

最近更新
01
ctr和crictl显示镜像不一致
03-13
02
alpine镜像集成常用数据库客户端
03-13
03
create-cluster
02-26
更多文章>
Theme by Vdoing | Copyright © 2015-2024 op81.com
苏ICP备18041258号-2
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式