天天看點

GoLang - Go中閉包用法

當我們不希望給函數起名字的時候,可以使用匿名函數,例如:func(x, y int) int { return x + y }。

這樣的一個函數不能夠獨立存在(編譯器會傳回錯誤:non-declaration statement outside function body),但可以被指派于某個變量,即儲存函數的位址到變量中:fplus := func(x, y int) int { return x + y },然後通過變量名對函數進行調用:fplus(3,4)。

當然,您也可以直接對匿名函數進行調用:func(x, y int) int { return x + y } (3, 4)。

下面是一個計算從 1 到 1 百萬整數的總和的匿名函數:

func() {

sum := 0

for i := 1; i <= 1e6; i++ {

sum += i

}

}()

表示參數清單的第一對括号必須緊挨着關鍵字 func,因為匿名函數沒有名稱。花括号 {} 涵蓋着函數體,最後的一對括号表示對該匿名函數的調用。

下面的例子展示了如何将匿名函數指派給變量并對其進行調用(function_literal.go):

package main

import “fmt”

func main() {

f()

}

func f() {

for i := 0; i < 4; i++ {

g := func(i int) { fmt.Printf("%d “, i) } //此例子中隻是為了示範匿名函數可配置設定不同的記憶體位址,在現實開發中,不應該把該部分資訊放置到循環中。

g(i)

fmt.Printf(” - g is of type %T and has value %v\n", g, g)

}

}

輸出:

0 - g is of type func(int) and has value 0x681a80

1 - g is of type func(int) and has value 0x681b00

2 - g is of type func(int) and has value 0x681ac0

3 - g is of type func(int) and has value 0x681400

我們可以看到變量 g 代表的是 func(int),變量的值是一個記憶體位址。

是以我們實際上擁有的是一個函數值:匿名函數可以被指派給變量并作為值使用。

練習 6.8 在 main 函數中寫一個用于列印 Hello World 字元串的匿名函數并指派給變量 fv,然後調用該函數并列印變量 fv 的類型。

匿名函數像所有函數一樣可以接受或不接受參數。下面的例子展示了如何傳遞參數到匿名函數中:

func (u string) {

fmt.Println(u)

}(v)

請學習以下示例并思考(return_defer.go):函數 f 傳回時,變量 ret 的值是什麼?

package main

import “fmt”

func f() (ret int) {

defer func() {

ret++

}()

return 1

}

func main() {

fmt.Println(f())

}

變量 ret 的值為 2,因為 ret++ 是在執行 return 1 語句後發生的。

這可用于在傳回語句之後修改傳回的 error 時使用。

defer 語句和匿名函數

關鍵字 defer (詳見第 6.4 節)經常配合匿名函數使用,它可以用于改變函數的命名傳回值。

匿名函數還可以配合 go 關鍵字來作為 goroutine 使用(詳見第 14 章和第 16.9 節)。

匿名函數同樣被稱之為閉包(函數式語言的術語):它們被允許調用定義在其它環境下的變量。閉包可使得某個函數捕捉到一些外部狀态,例如:函數被建立時的狀态。另一種表示方式為:一個閉包繼承了函數所聲明時的作用域。這種狀态(作用域内的變量)都被共享到閉包的環境中,是以這些變量可以在閉包中被操作,直到被銷毀,詳見第 6.9 節中的示例。閉包經常被用作包裝函數:它們會預先定義好 1 個或多個參數以用于包裝,詳見下一節中的示例。另一個不錯的應用就是使用閉包來完成更加簡潔的錯誤檢查(詳見第 16.10.2 節)。

繼續閱讀