if…else…
if boolean_expression {
// 分支1
} else if boolean_expression {
// 分支2
} else {
// 分支3
}
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
}
}
很明显,伪代码1的逻辑更容易理解,也更简洁。Go社区把这种if语句的使用方式称为if语句的“快乐路径”原则,所谓“快乐路径”也就是成功逻辑的代码执行路径,它的特点是这样的:
- 仅使用单分支控制结构;
- 当布尔表达式求值为false时,也就是出现错误时,在单分支中快速返回;
- 正常逻辑在代码布局上始终“靠左”,这样读者可以从上到下一眼看到该函数正常逻辑的全貌;
- 函数执行到最后一行代表一种成功状态
for
for 初始语句;条件表达式;结束语句 {
循环体代码
}
for i := 0; i < 10; i++ {
fmt.Println(i)
}
// 无限循环有3种方式,推荐第一种
for {
...
}
for true {
...
}
for ; ; {
...
}
for range
在go语言中,对于数组、切片、map以及channel等数据结构,用for range遍历起来会更方便
//遍历切片
s := []int{1, 2, 3}
for i, v := range s {
fmt.Println(i, v)
}
//遍历map
m := make(map[string]int)
m["a"] = 1
m["b"] = 2
m["c"] = 3
for k, v := range m {
fmt.Println(k, v)
}
for range 有几个常见的变种,以切片为例进行分析:
不关心元素的值时,可以省略代表元素值的变量v,只声明代表下标值的变量i
s := []int{1, 2, 3}
for i := range s {
fmt.Println(i) // 0 1 2
}
不关心下标,只关心元素值,那么可以用空标识符替代代表下标值的变量,这个空标识符不能省略,通常用下划线”_”表示
s := []int{1, 2, 3}
for _, v := range s {
fmt.Println(v) // 1 2 3
}
既不关心下标,也不关心元素值
s := []int{1, 2, 3}
for range s {
fmt.Println("test") // 打印3遍test
}
switch
n := 2
switch n {
case 1:
fmt.Println("n=1")
case 2:
fmt.Println("n=2") //
default:
fmt.Println("default")
}
探索switch执行次序
package main
import "fmt"
func case1() string {
fmt.Println("case1")
return "aaa"
}
func case2() string {
fmt.Println("case2")
return "return值相同则匹配成功, switch语句退出"
}
func case3() string {
fmt.Println("case3")
return "ccc"
}
func switchexpr() string {
fmt.Println("switchexpr")
return "return值相同则匹配成功, switch语句退出"
}
func main() {
switch switchexpr() {
case case1():
fmt.Println("main1")
case case2():
fmt.Println("main2")
case case3():
fmt.Println("main3")
default:
fmt.Println("default")
}
}
示例程序的执行结果为:
switchexpr
case1
case2
main2
上例中先拿到switchexpr()函数的返回值,再逐一与case语句中的函数返回值进行匹配,直到case语句中的某一函数的返回值与switchexpr()函数一致,打印对应逻辑,然后退出switch语句。示例中到case2函数就完成了匹配,所以其后面case3函数、default都不会再执行。
case语句支持表达式列表
func checkWorkday(a int) {
switch a {
case 1, 2, 3, 4, 5:
fmt.Println("it is a work day")
case 6, 7:
fmt.Println("it is a weekend day")
default:
println("default")
}
}
case1~case5匹配成功后,执行的都是case5中的代码逻辑,case6~case7匹配成功之后,执行的都是case7中的代码逻辑。
fallthough
如果需要强制执行下一个case的代码逻辑,可以显示使用Go提供的关键字fallthough的switch来实现。继续用前面的示例来举例:
package main
import "fmt"
func case1() string {
fmt.Println("case1")
return "aaa"
}
func case2() string {
fmt.Println("case2")
return "return值相同则匹配成功, switch语句退出"
}
func case3() string {
fmt.Println("case3")
return "ccc"
}
func switchexpr() string {
fmt.Println("switchexpr")
return "return值相同则匹配成功, switch语句退出"
}
func main() {
switch switchexpr() {
case case1():
fmt.Println("main1")
case case2():
fmt.Println("main2")
fallthrough
case case3():
fmt.Println("main3") // 如果没有fallthrough关键字,代码逻辑不会执行到这个case3
default:
fmt.Println("default")
}
}
示例程序的执行结果为:
switchexpr
case1
case2
main2
main3
有了fallthrough关键字,在执行完case2的语句后,switch语句没有退出,而是继续执行了下一个case,另外由于fallthrough的存在,Go不会执行case3函数,而是直接执行case3对应的代码分支。
另外还有一点要注意的是,如果某个case语句已经是switch语句中的最后一个case,并且它的后面也没有default分支,那么这个case中就不能再使用fallthrough,否则编译报错。