iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 6
0

Function

用英文寫程式久了,有一些專有名詞其實我不確定正式的中文是什麼。比方說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,有兩個返回值分別是[]stringerror。我們可以觀察到剛剛說的函數賦值給兩個變數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"

Reference


上一篇
條件執行與For循環
下一篇
命令行參數
系列文
啥物碗Golang? 30天就Go30

尚未有邦友留言

立即登入留言