[A Tour of Go 學習筆記] 03 流程控制
使用官方教學瞭解 Go 語言流程控制
初入 Golang 的世界,首先我們使用官方教學:A Tour of Go
來認識基本的 Golang 使用
這篇是流程控制相關的筆記,說明一些 if else for switch 還有 defer
for
Go 只有一種迴圈:for
組成結構是:
- 初始值:在第一次迭代之前執行
- 執行條件:在每次迭代前求值
- 更新語句:在每次迭代結束時執行
初始值通常是一個簡短變數聲明,該變數只作用於for
迴圈內
一旦執行條件判斷結果為false
則迴圈就會被停止
底下是一個基本的迴圈
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() {
sum := 1
for ; sum < 1000; {
sum += sum
}
fmt.Println(sum)
}
/*
>>> 1024
*/
甚至你可以連分號都省略
有沒有覺得很眼熟?這就是在 go 語言中實踐while
迴圈的方式
package main
import "fmt"
func main() {
sum := 1
for sum < 1000 {
sum += sum
}
fmt.Println(sum)
}
/*
>>> 1024
*/
如果想要寫出無窮迴圈,只要省略條件判斷迴圈就不會結束
如此就可以很簡單寫出無限迴圈
package main
func main() {
for {
}
}
if
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
*/
簡短語句
就跟for
一樣,if
也有簡短的寫法
可以在條件判斷式之前執行一個簡單的操作
可以參考底下範例
要注意的是變數v
的作用域只存在if
內
package main
import (
"fmt"
"math"
)
func pow(x, n, lim float64) float64 {
if v := math.Pow(x, n); v < lim {
return v
}
// 這裡就不能使用 v 了
return lim
}
func main() {
fmt.Println(
pow(3, 2, 10),
pow(3, 3, 20),
)
}
/*
>>> 9 20
*/
else
在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)
}
// can't use v here, though
return lim
}
func main() {
fmt.Println(
pow(3, 2, 10),
pow(3, 3, 20),
)
}
/*
>>> 27 >= 20
>>> 9 20
*/
switch
和其他語言不同的點是,go 語言中的 switch 不需要break
聲明
只會執行對應的 case,而其他語言會繼續執行後續的所有 case
在最後可以藉由default
定義所有條件都不符合的預設情況
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 Linux.
*/
case
中不一定要是常數或是整數,甚至可以在裡面執行計算
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?
>>> Too far away.
*/
不設定條件的 switch 就等同於switch true
,可以把很長的 if-then-else 變比較好閱讀
就由上到下比對 case 內條件何者首先為真,就執行內容
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
會將函數延遲到整個方法結束後執行
但是參數的值會先傳入,等到外層函數執行完成後才會被執行
package main
import "fmt"
func main() {
defer fmt.Println("world")
fmt.Println("hello")
}
/*
>>> hello
>>> world
*/
Stacking defers
如果在迴圈中使用 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
*/
首先可以先判斷迴圈外的兩個Println
會先被執行,這很好理解
接著進入迴圈,從 0 一路執行到 9,並且將印出當前數字送進defer
推遲執行
而實際印出的順序是從 9 印到 0,所以可以推斷defer
是一種堆疊
當多個defer
同時存在,則會遵守後進先出(LIFO)來執行