Go 流程控制語句:for、if、else、switch 和 defer
§ for
Go 只有一種循環結構:for
循環。
基本的 for
循環由三部分組成,它們用分號隔開:
- 初始化語句:在第一次迭代前執行
- 條件表達式,則前後的
;
會被去掉,若有:在每次迭代前求值 - 後置語句:在每次迭代的結尾執行
初始化語句通常爲一句短變量聲明,該變量聲明僅在 for
語句的作用域中可見。
一旦條件表達式的布爾值爲 false
,循環迭代就會終止。
注意:和 C、Java、JavaScript 之類的語言不同,Go 的 for 語句後面的三個構成部分外沒有小括號 ( )
, 而大括號 { }
則是必須的。
package main
import "fmt"
func main() {
sum := 0
for i := 0; i < 10; i++ {
sum += i
}
fmt.Println(sum)
}
輸出:
45
初始化語句和後置語句是可選的,例如:
package main
import "fmt"
func main() {
i := 0
j := 100
c := 0
for i != j { // for ;i != j; { 自動格式化後的結果,go 中的"while"
i++
j--
c += 1
}
fmt.Printf("i: %v, j: %v, c: %v\n", i, j, c)
}
輸出:
i: 50, j: 50, c: 50
如果只有條件表達式,則前後的 ;
會被 go fmt
去掉,成爲 go 中的"while"。若有初始化語句、條件表達式、後置語句中的兩個,則不會被省略。
for 也是 go 的 “while”
可以去掉分號,在 for
與 {
只寫條件語句。
C 的 while 在 Go 中也叫做 for
。
package main
import "fmt"
func main() {
s := 1
for s <= 1000 {
s += s
}
fmt.Println(s)
}
輸出:
1024
無限循環
如果省略循環條件,for
與 {
間什麼都不寫,該循環就不會結束,因此無限循環可以寫得很緊湊。
(沒有條件的 for 同 for true 一樣。)
package main
import (
"fmt"
"time"
)
func main() {
i := 0
for {
fmt.Println(i)
i++
time.Sleep(100 * time.Millisecond) // 暫停100毫米
}
}
輸出:
0
1
2
... // 省去部分輸出
^Csignal: interrupt // 鍵入了 control+C,以終止程序
§ if
Go 的 if 語句與 for 循環類似,表達式外無需小括號 ( )
,而大括號 { }
則是必須的。
package main
import (
"fmt"
"math"
)
func sqrt(x float64) string {
if x < 0 {
return sqrt(-x) + "i"
}
return fmt.Sprint(math.Sqrt(x))
}
func main() {
fmt.Println(sqrt(2), sqrt(-4))
}
輸出:
1.4142135623730951 2i
if 的簡短語句
同 for
一樣, if
語句可以在條件表達式前執行一個簡單的語句。
該語句聲明的變量作用域僅在 if
之內。
(在最後的 return
語句處使用 v
看看。)
package main
import (
"fmt"
"math"
)
func pow(x, n, lim float64) float64 {
if v := math.Pow(x, n); v < lim {
return v
}
return lim
}
func main() {
fmt.Println(
pow(3, 2, 10),
pow(3, 3, 20),
)
}
輸出:
9 20
if-else
package main
import (
"fmt"
"math"
)
func pow(x, n, lim float64) float64 {
if v := math.Pow(x, n); v < lim {
return v
} else {
fmt.Printf("%g >= %g\n", v, lim)
}
// 這裏開始就不能使用 v 了
return lim
}
func main() {
fmt.Println(
pow(3, 2, 10),
pow(3, 3, 20),
)
}
輸出:
27 >= 20
9 20
在 if
的簡短語句中聲明的變量同樣可以在任何對應的 else
塊中使用。
(在 main
的 fmt.Println
調用開始前,兩次對 pow
的調用均已執行並返回其各自的結果。)
§ switch
switch
是編寫一連串 if - else
語句的簡便方法。它運行第一個值等於條件表達式的 case 語句。
Go 的 switch
語句類似於 C、C++、Java、JavaScript 和 PHP 中的,不過 Go 只運行選定的 case
,而非之後所有的 case
。 實際上,Go 自動提供了在這些語言中每個 case
後面所需的 break
語句。 除非以 fallthrough
語句結束,否則分支會自動終止。 Go 的另一點重要的不同在於 switch
的 case
無需爲常量,且取值不必爲整數。
package main
import (
"fmt"
"runtime"
)
func main() {
fmt.Print("Go runs on ")
switch os := runtime.GOOS; os {
case "darwin":
fmt.Println("OS X.")
case "linux":
fmt.Println("Linux.")
default:
// freebsd, openbsd,
// plan9, windows...
fmt.Printf("%s.\n", os)
}
}
輸出:
Go runs on OS X.
switch 的求值順序
switch
的 case
語句從上到下順次執行,直到匹配成功時停止。
(例如,
switch i {
case 0:
case f():
}
在 i==0 時 f 不會被調用。)
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println("When's Saturday?")
today := time.Now().Weekday()
switch time.Saturday {
case today + 0:
fmt.Println("Today.")
case today + 1:
fmt.Println("Tomorrow.")
case today + 2:
fmt.Println("In two days.")
default:
fmt.Println("Too far away.")
}
}
輸出:
When's Saturday?
Tomorrow.
沒有條件的 switch
沒有條件的 switch
同 switch true
一樣。
這種形式能將一長串 if-then-else
寫得更加清晰。
package main
import (
"fmt"
"time"
)
func main() {
t := time.Now()
switch {
case t.Hour() < 12:
fmt.Println("Good morning!")
case t.Hour() < 17:
fmt.Println("Good afternoon.")
default:
fmt.Println("Good evening.")
}
}
輸出:
Good evening.
§ defer
defer
語句會將函數推遲到外層函數返回之後執行。
推遲調用的函數其參數會立即求值(閉包?🤔️),但直到外層函數返回前該函數都不會被調用。
package main
import "fmt"
func main() {
defer fmt.Println("world")
fmt.Println("hello")
}
輸出:
hello
world
defer 棧
推遲的函數調用會被壓入一個棧中。當外層函數返回時,被推遲的函數會按照後進先出的順序調用。
更多關於 defer
語句的信息,參考閱讀此文。
package main
import "fmt"
func main() {
fmt.Println("counting...")
for i := 0; i < 10; i++ {
defer fmt.Println(i)
}
fmt.Println("done.")
}
輸出:
counting...
done.
9
8
7
6
5
4
3
2
1
0