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)}//遍历mapm := make(map[string]int)m["a"] = 1m["b"] = 2m["c"] = 3for 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 := 2switch n {case 1:fmt.Println("n=1")case 2:fmt.Println("n=2") //default:fmt.Println("default")}
探索switch执行次序
package mainimport "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")}}
示例程序的执行结果为:
switchexprcase1case2main2
上例中先拿到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 mainimport "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")fallthroughcase case3():fmt.Println("main3") // 如果没有fallthrough关键字,代码逻辑不会执行到这个case3default:fmt.Println("default")}}
示例程序的执行结果为:
switchexprcase1case2main2main3
有了fallthrough关键字,在执行完case2的语句后,switch语句没有退出,而是继续执行了下一个case,另外由于fallthrough的存在,Go不会执行case3函数,而是直接执行case3对应的代码分支。
另外还有一点要注意的是,如果某个case语句已经是switch语句中的最后一个case,并且它的后面也没有default分支,那么这个case中就不能再使用fallthrough,否则编译报错。
