Go语言编程笔记3:控制流
一般来说,不同的编程语言之间控制流语句的差别应该是很小的,但Go语言的控制流语句有很多不同之处,值得单独进行讨论。
if
除了和其它语言类似的写法以外,Go语言可以在if
语句的条件中添加赋值语句:
package main
import "fmt"
func main() {
if a := 6; a < 10 {
fmt.Println("a<10")
}
}
当然上边这段示例的写法是没必要的,更常见的是通过函数调用获取结果后进行判断:
package main
import (
"fmt"
"log"
"strconv"
)
func main() {
checkStringNumber("12")
checkStringNumber("2.5")
checkStringNumber("lalala")
}
func checkStringNumber(strA string) {
if floatA, err := strconv.ParseFloat(strA, 64); err != nil {
log.Fatal(err)
} else if floatA > 10 {
fmt.Println("a>10")
} else {
fmt.Println("a<=10")
}
}
// a>10
// a<=10
// 2021/11/07 15:38:50 strconv.ParseFloat: parsing "lalala": invalid syntax
当然这种方式只是给你提供了一种选择,并非必须,不过这么做可能带来的好处就是,将通过函数调用的返回值的使用范围局限到一个if
语句的作用域内,而不会污染外部的函数作用域。如果你遇到一些变量命名冲突的问题可以尝试用这种方式解决。
此外,Go语言对一些格式有严格要求,比如常见的if...else
写法:
package main
import "fmt"
func main() {
if 1 < 5 {
fmt.Println("1<5")
}
else{
fmt.Println("1>=5")
}
}
在Go语言中是不允许的,因为Go要求else
必须要和if
块的}
符号在同一行:
package main
import "fmt"
func main() {
if 1 < 5 {
fmt.Println("1<5")
} else {
fmt.Println("1>=5")
}
}
怎么说呢,我一直都用的不允许的那种书写风格…我只能说,Go语言的作者大概是个强迫症吧。
顺带一提,Go语言中是可以给if
语句中的条件表达式加上()
的,但没必要,因为官方的格式化工具会在格式化代码时强制将不必要的()
删除,你大概会写了个寂寞。
for
Go语言中只有for
语句用于循环,没有while
语句。此外for
也仅有一种形式,即最经典的for 循环变量初始化;循环条件;循环变量步进
。
这里展示一个实际例子:
package main
import "fmt"
func main() {
for i := 0; i < 10; i++ {
fmt.Printf("%d ", i)
}
fmt.Println()
}
// 0 1 2 3 4 5 6 7 8 9
特别的,可以将循环变量初始化和步进语句省略,在这种情况下for
循环更像是其它语言中的while
语句:
package main
import "fmt"
func main() {
i := 0
for i < 10 {
fmt.Printf("%d ", i)
i++
}
fmt.Println()
}
// 0 1 2 3 4 5 6 7 8 9
此时i<10
左右两侧的;
写不写都一样,格式化工具会自动删除,所以最好还是别写。
甚至可以完全删除for
所有的三个表达式:
package main
import "fmt"
func main() {
i := 0
for {
if i >= 10 {
break
}
fmt.Printf("%d ", i)
i++
}
fmt.Println()
}
// 0 1 2 3 4 5 6 7 8 9
此时的for
语句事实上就是一个无限循环语句,除非使用break
跳出,否则会一直循环下去。
其它比较常见的用法还有结合range
关键字来迭代数组或切片:
package main
import "fmt"
func main() {
numbers := []int{1, 2, 3, 4, 5, 6}
for index, value := range numbers {
fmt.Printf("numbers[%d]=%d\n", index, value)
}
}
// numbers[0]=1
// numbers[1]=2
// numbers[2]=3
// numbers[3]=4
// numbers[4]=5
// numbers[5]=6
通道也可以使用这种方式来迭代,关于通道,会在后边介绍。
for
语句的作用域问题也是个会容易忽视的问题,该内容已经在之前Go语言编程笔记2:变量中讨论过了,这里不再赘述。
switch
表面上看Go语言的switch
语句与其它语言类似,其实有很大的不同:
package main
import "fmt"
func main() {
login("lalala")
login("root")
login("apple")
}
func login(name string) {
switch name {
case "apple":
fmt.Println("welcome")
case "root":
fmt.Println("don't permit use root account login")
default:
fmt.Println("it's a unregistry account")
}
}
// it's a unregistry account
// don't permit use root account login
// welcome
看上去似乎除了没有使用break
以外和其它语言的switch
语句差别不大,但其实有很大差别。
一般性的switch
语句的执行流程是依次匹配所有的case
条件,如果满足就执行相应的case
块,执行过程中如果有break
语句就跳出switch
,如果没有,在结束case
块后会继续switch
的逻辑向下匹配。
但Go语言中的switch
不会,它会像自带break
那样,一旦匹配到某个case
条件,执行完相应的case
块,就会结束整个switch
语句。
此外,我们依然是可以在switch
中显式地执行break
的:
package main
import "fmt"
func main() {
login("lalala")
login("root")
login("apple")
}
func login(name string) {
switch name {
case "apple":
break
fmt.Println("welcome")
case "root":
fmt.Println("don't permit use root account login")
default:
fmt.Println("it's a unregistry account")
}
}
// it's a unregistry account
// don't permit use root account login
Go语言的switch
语法中还有一个奇怪的关键字fallthrough
:
package main
import "fmt"
func main() {
login("lalala")
login("root")
login("apple")
}
func login(name string) {
switch name {
case "apple":
fmt.Println("welcome")
fallthrough
case "root":
fmt.Println("don't permit use root account login")
default:
fmt.Println("it's a unregistry account")
}
}
// it's a unregistry account
// don't permit use root account login
// welcome
// don't permit use root account login
如果在某个case
块的结尾使用fallthrough
,程序会自动跳转到下一个case
块中执行,无论下一个case
块的匹配条件是什么。我并不清楚这个关键字在实际工作中有什么使用场景,所以这里不过多讨论。
除了一般性的利用switch
进行值匹配外,switch
还可以检测变量的类型:
package main
import "fmt"
func main() {
checkType("hello")
checkType("1")
checkType(1)
checkType(2.5)
}
func checkType(variable interface{}) {
var varForm string
switch variable.(type) {
case int:
varForm = "int"
case float64, float32:
varForm = "float"
case string:
varForm = "string"
default:
varForm = "not known"
}
fmt.Printf("%v 's type is %s\n", variable, varForm)
}
// hello 's type is string
// 1 's type is string
// 1 's type is int
// 2.5 's type is float
特别的,如果用switch
匹配多个接口类型,是有可能同时满足多个接口的,此时应当遵循“先精细后粗略”的顺序排布case
条件,这部分内容会在后续接口部分的笔记介绍。