Byte Ebi's Logo

Byte Ebi 🍤

每天一小口,蝦米變鯨魚

[A Tour of Go 學習筆記] 03 流程控制

使用官方教學瞭解 Go 語言流程控制

Ray

初入 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)來執行

最新文章

Category

Tag