用英文寫程式久了,有一些專有名詞其實我不確定正式的中文是什麼。比方說Function應該翻譯為函示、函數、方法(?),之後統一稱之為「函數」。總之,我們今年來聊一聊Go裡面的Function。
函數包含幾個部分,分別是:函數名、參數、返回值(可省略)以及函數本體。
func name(parameter-list) (result-list) {
body
}
go語言有一些內建的函數,例如得到長度的len()
會返回int
。每個go專案至少會有一個main()
函數,是程式的主要進入點。除了main函數以外,最後一行會加上return
回傳返回值;就算沒有返回值,也需要放一個return。我們來看一些例子:
// main 函數
func main() {
fmt.Println(sum(1, 2)) // 印出3
}
// 有回傳值的函數
func sum(a, b int) int {
return a + b
}
也可以使用匿名函數的方式,把函數傳給一個變數:
greeting := func(name string) {
fmt.Println("Hi " + name)
}
greeting("Bater") // 會輸出"Hi Bater"
有時候會遇見沒有函數實體的函數,這代表函數不是以go實踐的(可能是c):
package math
func Sin(x float64) float
返回值可以不只一個,在實務上蠻常使用到的。如果返回的變數有不需要的部分,可以使用_
去接,代表可以省略或忽略。我們來看一個稍微複雜一點的例子:
func main() {
for _, url := range os.Args[1:] {
links, err := findLinks(url)
if err != nil {
fmt.Fprintf(os.Stderr, "findlinks2: %v\n", err)
continue
}
for _, link := range links {
fmt.Println(link)
}
}
}
// findLinks performs an HTTP GET request for url, parses the
// response as HTML, and extracts and returns the links.
func findLinks(url string) ([]string, error) {
resp, err := http.Get(url)
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
resp.Body.Close()
return nil, fmt.Errorf("getting %s: %s", url, resp.Status)
}
doc, err := html.Parse(resp.Body)
resp.Body.Close()
if err != nil {
return nil, fmt.Errorf("parsing %s as HTML: %v", url, err)
}
return visit(nil, doc), nil
}
雖然看起來有點可怕,但其實大部分的概念前幾天都已經解釋過了,一行一行應該可以慢慢理解。我們先觀察函數findLinks
,他有一個參數url
型態為string,有兩個返回值分別是[]string
與error
。我們可以觀察到剛剛說的函數賦值給兩個變數links, err := findLinks(url)
,也可以在稍後的片段for迴圈中,看到將不需要的回傳值傳遞給_
變數。
除了回傳變數,回傳值也可以是另一個函數,我們來看另一個例子:
package main
import "fmt"
func getSequence() func() int {
i:=0
return func() int {
i+=1
return i
}
}
func main(){
/* nextNumber 是一個函數, i 為 0 */
nextNumber := getSequence()
/* 呼叫 nextNumber 函數,i 遞增 */
fmt.Println(nextNumber())
fmt.Println(nextNumber())
fmt.Println(nextNumber())
/* 呼叫一個新的 nextNumber1 函數 */
nextNumber1 := getSequence()
fmt.Println(nextNumber1())
fmt.Println(nextNumber1())
}
最後會印出結果:
1
2
3
1
2
參數可以不固定數量,稱之為可變參數函數。最典型的例子就是fmt.Printf
,他會接收一個主要參數,之後接收任意多個次要參數。在宣告參數數量可變的時候,在參數類型之前加上...
代表數量可變。這樣該函數就會接受任意數量的參數,我們來看一個例子:
func sum(vals...int) int {
total := 0
for _, val := range vals {
total += val
}
return total
}
sum
會返回任意個整數的和。在函數內,參數vals
被視為[]int
的Array。我們來看看使用實例:
fmt.Println(sum()) // "0"
fmt.Println(sum(3)) // "3"
fmt.Println(sum(1, 2, 3, 4)) // "10"
如果原始變數就是Array,放進sum
的時候後面要加上...
:
vals := []int{1, 2, 3, 4}
fmt.Println(sum(values...)) // "10"