GO語言————6.8 閉包

FLy_鵬程萬里發表於2018-06-30

6.8 閉包

當我們不希望給函式起名字的時候,可以使用匿名函式,例如: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 節)。



相關文章