函式能讓你將程式碼組成可重複使用的單位。
它由func關鍵字、函式的名稱,以及以小括號包住、以逗號分隔的零個或多個參數和型別所組成。
所有參數都必須明確標註型別;參數沒有型別推斷。 參數也沒有預設值,所以所有函式參數都是必填的。
import "fmt"
// No parameters
func PrintHello() {
fmt.Println("Hello")
}
// Two parameters
func PrintGreetingName(greeting string, name string) {
fmt.Println(greeting + " " + name)
}
型別相同的參數可以一起宣告,後面接著單一的型別宣告。
import "fmt"
func PrintGreetingName(greeting, name string) {
fmt.Println(greeting + " " + name)
}
讓我們快速說明兩個經常被混為一談的詞:parameters和arguments。
函式參數是定義在函式簽章中的名稱,例如上方PrintGreetingName函式中的greeting和name。
函式引數則是我們呼叫函式時,實際傳給函式參數的具體值。
例如,在下面的範例中,"Hello"和"Katrina"就是傳給greeting和name參數的引數:
PrintGreetingName("Hello", "Katrina")
函式參數之後接著零個或多個回傳值,這些回傳值也必須明確標註型別。
單一的回傳值不加括號,多個回傳值則以括號包住。
函式使用return關鍵字將值回傳給呼叫端的程式碼。
一個函式中可以有多個return敘述。
函式的執行會在遇到其中一個return敘述時立刻結束。
如果要從函式回傳多個值,值與值之間以逗號分隔。
func Hello(name string) string {
return "Hello " + name
}
func HelloAndGoodbye(name string) (string, string) {
return "Hello " + name, "Goodbye " + name
}
呼叫函式時,要指定函式名稱,並在小括號中為函式的每一個參數傳入引數。
import "fmt"
// No parameters, no return value
func PrintHello() {
fmt.Println("Hello")
}
// Called like this:
PrintHello()
// One parameter, one return value
func Hello(name string) string {
return "Hello " + name
}
// Called like this:
greeting := Hello("Dave")
// Multiple parameters, multiple return values
func SumAndMultiply(a, b int) (int, int) {
return a+b, a*b
}
// Called like this:
aplusb, atimesb := SumAndMultiply(a, b)
除了參數之外,回傳值也可以選擇性地命名。
如果使用了具名回傳值,那麼不帶引數的return敘述就會回傳這些值。
這就是所謂的「裸回傳」。
func SumAndMultiplyThenMinus(a, b, c int) (sum, mult int) {
sum, mult = a+b, a*b
sum -= c
mult -= c
return
}
釐清傳值與傳參考的概念也很重要。
首先,讓我們釐清傳值。
在下面的範例中,當我們把變數val傳給函式MultiplyByTwo時,傳入的是val的複本。
因此,newVal會是更新後的值4,但原始的變數val仍然是2。
在背後,Go 基本上會複製原始值,因此只有這個複本(也就是v)會被函式修改。
val := 2
func MultiplyByTwo(v int) int {
v = v * 2
return v
}
newVal := MultiplyByTwo(val)
// newval is 4, val is still 2 because only a copy of its value was passed into the function
嚴格來說,在 Go 中所有引數都是傳值,也就是會複製提供給函式的值或資料。
但如果你不想複製傳入函式的資料,並且想在函式中修改這些資料,
就應該改用pointers作為引數,也就是傳參考。
我們使用pointer來達成傳參考。
將pointer引數傳入函式之後,
我們就能修改傳入函式的底層資料,而不只是對資料的複本進行操作。
目前你只需要知道,指標型別可以透過函式簽章中型別前面的*來辨認。
func HandlePointers(x, y *int) {
// Some logic to handle integer values referenced by pointers x and y
}
如果pointer的概念讓你感到困惑,別擔心。
我們在課程大綱後面的內容有專門的章節,可以幫助你精通指標。
請注意,slices和maps是上述規則的例外。
當我們把slice或map當作引數傳入函式時,即使型別中沒有明確的*,它們也會被視為指標型別。
這表示如果我們把 slice 或 map 傳入函式並修改其底層資料,
這些變更會反映在原始的 slice 或 map 上。
在這個練習中,你會再寫一些程式碼,內容跟準備和烹調你最愛食譜裡的極品千層麵有關。
這次有四項任務。 第一項跟烹調本身有關,另外三項則是關於完美的準備工作。
為了下一份要做的千層麵,你想確保預留了足夠的時間,好讓你能好好享受烹調的過程。 你已經規劃好千層麵要有哪些層了。 現在你想據此估算準備工作會花多久時間。
請實作一個函式 PreparationTime,它接受以 []string 表示的層的切片,以及每層的平均準備時間(以分鐘為單位)作為 int。
這個函式應該根據層數,回傳預估的總準備時間,型別為 int。
Go 的函式沒有預設值。
如果傳入的平均準備時間是 0(int 的預設初始值),就應該採用預設值 2。
layers := []string{"sauce", "noodles", "sauce", "meat", "mozzarella", "noodles"}
PreparationTime(layers, 3)
// => 18
PreparationTime(layers, 0)
// => 12
除了預留時間之外,你也想確保有足夠的醬汁和麵條,好做出你夢想中的千層麵。 千層麵中每一層麵條層,需要 50 公克的麵條。 每一層醬汁層,則需要 0.2 公升的醬汁。
定義函式 Quantities,它接受以 []string 表示的層的切片作為參數。
這個函式接著會算出做這道料理所需的麵條和醬汁數量。
結果應該以兩個值回傳:noodles 為 int,sauce 為 float64。
Quantities([]string{"sauce", "noodles", "sauce", "meat", "mozzarella", "noodles"})
// => 100, 0.4
前陣子你去拜訪一位朋友,並在他家吃了千層麵。 那千層麵非常棒,而且有股特別的風味。 朋友把食材陣列寄給你,並告訴你陣列上的最後一項,就是讓那頓飯如此特別的「祕密食材」。 現在你也想把这个祕密食材加進自己的食譜裡。
撰寫一個函式 AddSecretIngredient,它接受兩個型別為 []string 的食材切片作為參數。
第一個參數是朋友寄給你的陣列,第二個是你自己食譜的食材陣列。
你的食材陣列中,最後一個元素一定是 "?"。
這個函式應該把它替換成你朋友陣列中的最後一項。
注意: AddSecretIngredient 不會回傳任何東西,你應該直接修改你自己的食材陣列。
朋友的食材陣列則不該被修改。
另外,由於 slice 是以指標的形式傳入函式,傳進 AddSecretIngredient 的兩個 []string 引數若有變動,會直接生效。
friendsList := []string{"noodles", "sauce", "mozzarella", "kampot pepper"}
myList := []string{"noodles", "meat", "sauce", "mozzarella","?"}
AddSecretIngredient(friendsList, myList)
// myList => []string{"noodles", "meat", "sauce", "mozzarella", "kampot pepper"}
你的食譜上列出的份量,只夠做出兩人份的千層麵。 因為你下次想煮給更多人吃,所以你想算出不同份數所需的份量。
請實作一個函式 ScaleRecipe,它接受兩個參數。
float64 的切片,內含兩人份所需的份量。這個函式應該回傳一個 float64 切片,內含所需份數的份量。
不過,你想保留原本的食譜。
這表示 quantities 這個引數不應該在這個函式中被修改。
quantities := []float64{ 1.2, 3.6, 10.5 }
scaledQuantities := ScaleRecipe(quantities, 4)
// => []float64{ 2.4, 7.2, 21 }