流控制
golang精簡了控制語句,但足夠我們使用。
if...else 語句需要注意一點的是對初始化語句的支持。
if x := xtest(); x == 0 { // 先執行xtest函數,再對x==0條件表示式判斷布爾值
fmt.Println(x)
}
switch...case語句同樣對初始化語句支持,此外不能出現重複的cacs值,單個case支持多條件匹配。
package main
import "fmt"
func xtest(x int) {
switch y := x + 1; y {
case 1, 2, 3: // 當x滿足其中一項時即匹配
fmt.Println("1|2|3")
case 4:
fmt.Println("4")
case 5: // 該語句等同於case5 : break
default:
fmt.Println("Unkown")
}
}
func main() {
xtest(2)
xtest(3)
xtest(4)
}
switch可以省略條件表達式,默認爲true。
package main
import "fmt"
func xtest(x int) {
switch { // 等同於 switch true
case x > 0:
fmt.Println("Positive", x)
case x < 0:
fmt.Println("Nagetive", x)
default:
fmt.Println("Zero", x)
}
}
func main() {
xtest(0)
xtest(1)
xtest(-1)
}
需要注意的是switch的條件表達式如果未false時,case匹配規則剛好相反,爲不滿足case的條件表達式時匹配。
package main
import "fmt"
func xtest(x int) {
switch false {
case x >= 0:
fmt.Println("Positive", x)
case x <= 0:
fmt.Println("Nagetive", x)
default:
fmt.Println("Zero", x)
}
}
func main() {
xtest(0)
xtest(1)
xtest(-1)
}
switch..case語句無需顯示執行break語句,case執行完畢後自動跳出,如果需要繼續執行下一個的case,可使用fallthrough,但不再匹配後續case條件表達式。fallthrough必須放在case塊結尾.。
package main
import "fmt"
func xtest(x int) {
switch {
case x > 0:
fmt.Println("Positive", x)
fallthrough
case x < 0:
fmt.Println("Nagetive", x)
fallthrough
default:
fmt.Println("Zero", x)
}
}
func main() {
xtest(3)
}
golang中循環語句只有for一種,但是for的條件表達式有多種形式。
package main
import "fmt"
func main() {
for i := 0; i < 2; i++ { // 常用方式
fmt.Println("i=", i)
}
j := 0
for j < 2 { // 相當於 for _; j< 2; _
fmt.Println("j=", j)
j++
}
for { // 相當於for true
break
}
i := []string{"hello", "world"}
for k, s := range i { // 遍歷s切片,k可以替換成_
fmt.Println(k, s)
}
}
函數
golang函數使用func定義函數,需要注意幾點特性
1)函數無需前置聲明
2)函數不支持嵌套定義
3)函數不支持重載
4)函數支持不定參數
5)函數支持多返回值
6)函數支持命名返回值
7)函數支持匿名函數和閉包
函數定義包括函數名、參數列表、返回值列表(可省略)和函數體。
func name(parameter list)(rusult list){
body
}
函數在golang中作爲第一對象,可以作爲參數、返回值、變量。相同返回值列表和參數列表的函數被認作一種數據類型。
package main
import "fmt"
type priInt func(int) int
func a(x int) int {
fmt.Println("Func a", x)
x++
return x
}
func b(x int) int {
fmt.Println("Func b", x)
return x
}
func test(x priInt) priInt {
x(1)
return x
}
func main() {
var funtest priInt
funtest = test(a)
funtest(2)
funtest = test(b)
funtest(2)
}
golang函數的參數列表支持相鄰同類型合併,golang函數參數列表支持不定長參數,本質上不定長參數是切片。切片必須放在參數列表尾部。
package main
import "fmt"
func test(x, y int, a ...string) {
fmt.Println(x, y, a)
}
func main() {
test(1, 2, "hello", "abc")
}
golang函數支持多返回值,並且函數支持命名返回值,即返回值變量作爲局部變量使用方式和參數一致。
package main
import "fmt"
func test1() (int, int, int) { // 多返回值函
return 1, 2, 3
}
func test2() (x int, y string) {
x = 1
y = "hello"
return // 隱式轉化 相當於 return x, y
}
func main() {
a, b, _ := test1() // 多餘不需要的返回值可使用_替換
fmt.Println(a, b)
c, d := test2()
fmt.Println(c, d)
}
golang函數支持匿名函數,與正常函數相比,減去了函數名稱。匿名函數常用在函數內部嵌套上,匿名函數可以賦值給變量,或者作爲參數、返回值。
package main
import "fmt"
func test(a func(int)) func(string) { // 匿名函數作爲參數和返回值
a(1)
return func(str string) {
fmt.Println(str)
}
}
func main() {
func() {
fmt.Println("hello")
}() //聲明匿名函數並直接調用
a := func() {
fmt.Println("world")
} //聲明匿名函數並賦值給變量
a()
b := test(func(i int) {
fmt.Println("abc")
})
b("123")
}
Golang函數支持閉包特性,閉包指的是函數在使用中引用了函數上下文的環境。閉包會使上下文環境聲明週期變長,如果上下文中定義了局部變量,該變量將被分配到堆中。
package main
import "fmt"
func test() func() {
x := 1
fmt.Println(x, &x)
return func() {
fmt.Println(x, &x) // 匿名函數引用了上下文變量x
}
}
func main() {
a := test()
a()
}
golang函數支持延時調用,當函數運行流程結束後,開始執行延時調用函數。使用defer可以向當前函數註冊演示調用函數,當函數註冊了多個延時調用函數,其執行順尋是後註冊的先執行。
package main
import "fmt"
func test() int {
x := 1
defer func() {
x++
fmt.Println("defer", x)
}()
defer func() {
x++
fmt.Println("defer2", x) // 先執行
}()
return x // 執行完後開始執行延時調用
}
func main() {
fmt.Println(test())
}
golang函數使用painc和recover實現類似try...catch的結構化異常捕捉。painc會立即終止當前函數流程,並執行當前函數延遲調用函數。
在延時調用函數中使用recover捕捉painc提交的錯誤對象。需要注意的是golang程序異常時,使用painc代表着程序終止。
package main
import (
"fmt"
"log"
)
func main() {
defer func() {
if err := recover(); err != nil { // 捕捉異常
log.Fatalln(err)
}
}()
panic("This is painc") // 終止函數流程
fmt.Println("main") // 不會執行
}