Golang簡單入門教程——函數進階篇

本文始發於個人公眾號:TechFlow,原創不易,求個關注

今天是golang專題的第八篇,我們來聊聊golang當中的函數。

我們在之前的時候已經介紹過了函數的基本用法,知道了怎麼樣設計或者是定義一個函數,以及怎麼樣調用一個函數,還了解了defer的用法。今天這篇文章我們來繼續深入這個話題,來看看golang當中關於函數的一些進階的用法。

返回error

前文當中我們曾經提到過,在golang當中並沒有try catch捕獲異常的機制。在其他語言當中異常只有一種,可以通過try catch語句進行捕獲,而golang當中做了區分,將異常分為兩種,一種是可以在函數當中返回的error,另外一種是嚴重的會引起程序崩潰的panic

在golang中,error也是一個數據類型,由於golang支持函數的多值返回,所以我們可以設置一個返回值是error。我們通過對這個error的判斷來獲取運行函數的情況。

舉個例子,比如說,假設我們實現一個Divide函數實現兩個int相除。那麼顯然我們需要除數不能為0,當除數為0的時候我們需要返回一個異常。這個時候我們可以把代碼寫成這樣:

// Divide test
func Divide(a, b int) (ret int, err error) {
 if b == 0 {
  err = errors.New("divisor is zero")
  return
 }
 return a / b, nil
}

當我們調用函數的時候,我們用兩個變量去接收這個函數返回的結果,第二個變量的類型是error。當這個函數成功執行的時候第二個變量的結果為nil,我們只需要判斷它是否等於nil,就可以知道函數執行是否成功。如果不成功,我們還可以記錄失敗的原因。

func main() {
 ret, err := Divide(5, 2)
 if err == nil {
  fmt.Println(ret)
 } else {
  fmt.Println(err)
 }
}

這種用法在golang當中非常常見,我們之前在介紹字符串相關操作的時候也介紹過返回error的用法。我們在設計函數的時候如果需要判斷輸入的合法性可以使用error,這樣就可以保證handle住非法的情況,並且也能讓下游感知到。

不定參數

不定參數的用法在很多語言當中都有,比如在Python當中,不定參數是*args。通過*args我們可以接受任何數量的參數,由於Python是弱變量類型的語言,所以args這些參數的類型可以互不相同。但是golang不行,golang嚴格限制類型,不定參數必須要保證類型一樣。除此之外,其他的用法和Python一樣,不定參數會以數組的形式傳入函數內部,我們可以使用數組的api進行訪問。

我們來看一個例子,我們通過…來定義不定參數。比如我們可以實現一個sum函數,可以將任意個int進行累加。

func Sum(nums ... int) int{
    ret := 0
    for _, num := range nums {
        ret += num
    }
    return ret
}

我們來仔細研究一下上面這個例子,在這個例子當中,我們通過…傳入了一個不定參數,我們不定參數的類型只寫一次,寫在…的後面。從底層實現的機制上來說,不定參數本質上是將傳入的參數轉化成數組的切片。但是這就有了一個問題,既然傳入的是一個數組的切片,我們為什麼要專門設置一個關鍵字,而不是規定傳入一個切片呢?

比如上面的代碼我們完全可以寫成這樣:

func Sum(nums []int) int{
    ret := 0
    for _, num := range nums {
        ret += num
    }
    return ret
}

無論從代碼的閱讀還是編寫上來看相差並不大,好像這樣做完全沒有意義,其實不是這樣的。這個關鍵字簡化的並不是函數的設計方,而是函數的使用方。如果我們規定了函數的輸入是一個切片,那麼當我們在傳入數據的時候,必須要使用強制轉化,將我們的數據轉化成切片,比如這樣:

Sum([]int(3, 4, 6, 8))

而使用…關鍵字我們則可以省略掉強制轉化的過程,上面的代碼我們寫成這樣就可以了:

Sum(3, 4, 6, 8)

很明顯可以看出差異,使用不定參數的話調用方會輕鬆很多,不需要再進行額外的轉換。如果我們要傳入的也是一個數組,那麼在傳遞的時候也需要用…符號將它展開

a := make([]int)
a = append(a, 3)
a = append(a, 4)
Sum(a...)
Sum(a[1:]...)

既然聊到不定參數的傳遞,那麼又涉及到了一個問題,當我們想要像Python那樣傳遞多個類型不同的參數的時候,應該怎麼辦呢?按照道理golang是靜態類型的語言,限制死了參數的類型,是不能隨便轉換的才對。但是偏偏這樣操作是可以的,因為golang當中有一個特殊的類型,叫做interface

interface的用法很多,一個很重要的用法是用在面向對象當中充當結構體的接口。這裏我們不做過多深入,我們只需要知道,interface的一個用法是可以用來代替所有類型的變量。我們來看一個例子:

func testInterface(args ...interface{}) {
    for _, arg := range args {
        switch arg.(type) {
            case int:
             fmt.Println("it's a int")
         case string:
             fmt.Println("it's a string")    
            case float32:
             fmt.Println("it's a float")
            default:
             fmt.Println("it's an unknown type")
        }
    }
}


func main() {
    testInterface(3, 4.5, "abc")
}

我們可以用.(type)獲取一個interface變量實際的類型,這樣我們就實現了任意類型任意數量參數的傳入。

匿名函數和閉包

匿名函數我們在Python當中經常使用到,其實這個概念出現已久,最早可以追溯到1958年Lisp語言。所以這並不是一個新鮮的概念,只是傳統的C、C++等語言沒有支持匿名函數的功能,所以顯得好像是一個新出現的概念一樣。golang當中也支持匿名函數,但是golang當中匿名函數的使用方式和Python等語言稍稍有些不同。

在Python當中我們是通過lambda關鍵字來定義匿名函數,它可以被傳入另一個函數當中,也可以賦值給一個變量。golang當中匿名函數的定義方式和普通函數基本是一樣的,只是沒有函數名而已,不過它也可以被傳入函數或者是賦值給另一個變量。

比如:

s := func(a, b int) int {
    return a + b
}

c := s(3, 4)

除了匿名函數之外,golang還支持閉包。閉包的概念我們在之前Python閉包的介紹當中曾經提到過,我們之前也用過好幾次,閉包的本質不是一個包,而是一個函數,是一個持有外部環境變量的函數。比如在Python當中,我們經常可以看到這樣的寫法:

def outside(x):
    def inside(y):
        print(x, y)
    return inside


ins = outside(3)
ins(5) #3, 5

我們可以看到outside這個函數返回了inside這個函數,對於inside這個函數而言,它持有了x這個變量。x這個變量並不是屬於它的,而是定義在它的外部域的。並且我們在調用inside的時候是無法干涉這個變量的,這就是一個閉包的典型例子。根據輪子哥的說法,閉包的閉的意思並不是封閉內部,而是封閉外部。當外部scope失效的時候,函數仍然持有一份外部的環境的值。

golang當中閉包的使用方法大同小異,我們來看一個類似的例子:

func main() {
    a := func(x int) (func(int)) {
        return func(y int){
            fmt.Println(x, y)
        }
    }
    b := a(4)
    b(5)
}

這個閉包的例子和剛才上面Python那個例子是一樣的,唯一不同的是由於golang是強類型的語言,所以我們需要在定義閉包的時候將輸入和輸出的類型定義清楚。

總結

關於golang當中函數的高級用法就差不多介紹完了,這些都是實際編程當中經常使用的方法,如果想要學好golang這門語言的話,這些是基本功。如果你之前有其他語言的基礎,來寫go的話,整體上手的難度還是不大的,很多設計都可以在其他的語言當中找到影子,有了參照來學會簡單得多。

我很難描述實際工作當中寫golang的體驗,和我寫任何一門其他的語言都不一樣,有一種一開始期望很低,慢慢慢慢總能發現驚喜的感覺。我強烈建議大家去實際感受一下。

如果喜歡本文,可以的話,請點個關注,給我一點鼓勵,也方便獲取更多文章。

本文使用 mdnice 排版

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化