成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Go語言中的閉包:封裝數據與功能的強大工具

開發 前端
在 Go 編程中,閉包是一個強大的工具,可用于用函數封裝數據,并創建生成器和迭代器等。它們提供了一種訪問函數體外定義的變量的方法,即使在函數返回后也是如此。

閉包是包括 Go 在內的編程語言的一項強大功能。通過閉包,您可以在函數中封裝數據,并通過函數的返回值訪問這些數據。在本文中,我們將介紹 Go 中閉包的基礎知識,包括它們是什么、如何工作以及如何有效地使用它們。

什么是閉包?

go官方有一句解釋:

Function literals are closures: they may refer to variables defined in a surrounding function. Those variables are then shared between the surrounding function and the function literal, and they survive as long as they are accessible.

翻譯過來就是:

函數字面量(匿名函數)是閉包:它們可以引用在周圍函數中定義的變量。然后,這些變量在周圍的函數和函數字面量之間共享,只要它們還可以訪問,它們就會繼續存在。

閉包是一種創建函數的方法,這些函數可以訪問在其主體之外定義的變量。閉包是一個可以捕捉其周圍環境狀態的函數。這意味著函數可以訪問不在其參數列表中或在其主體中定義的變量。閉包函數可以在外部函數返回后訪問這些變量。

在 Go 中創建閉包

在 Go 中,您可以使用匿名函數創建閉包。創建閉包時,函數會捕獲其周圍環境的狀態,包括外部函數中定義的任何變量。閉包函數可以在外部函數返回后訪問這些變量。

下面是一個在 Go 中創建閉包的示例:

func adder() func(int) int { // 外部函數
 sum := 0
 return func(x int) int { // 內部函數
  fmt.Println("func sum: ", sum)
  sum += x
  return sum
 }
}

func main() {
 a := adder()
 fmt.Println(a(1))
 fmt.Println(a(2))
 fmt.Println(a(3))
}

在本例中,我們定義了一個返回匿名函數的加法器函數。匿名函數捕捉加法器函數中定義的 sum 變量的狀態。每次調用匿名函數時,它都會將參數加到求和變量中,并返回結果。

所以其輸出結果為:

func sum:  0
1
func sum:  1
3
func sum:  3
6

在 Go 中使用閉包

在 Go 中,閉包可用于多種用途,包括用函數封裝數據、創建生成器、迭代器和 memoization 函數。

下面是一個使用閉包將數據與函數封裝在一起的示例:

func makeGreeter(greeting string) func(string) string {
 return func(name string) string {
  fmt.Printf("func greeting: %s, name: %s\n", greeting, name)
  return greeting + ", " + name
 }
}

func main() {
 englishGreeter := makeGreeter("Hello")
 spanishGreeter := makeGreeter("Hola")

 fmt.Println(englishGreeter("John"))
 fmt.Println(englishGreeter("Tim"))
 fmt.Println(spanishGreeter("Juan"))
 fmt.Println(spanishGreeter("Taylor"))
}

在本例中,我們定義了一個名為 makeGreeter 的函數,它返回一個匿名函數。該匿名函數接收一個字符串參數,并返回一個將問候語和名稱連接起來的字符串。我們創建了兩個問候語程序,一個用于英語,一個用于西班牙語,然后用不同的名稱調用它們。

所以其輸出為:

func greeting: Hello, name: John
Hello, John
func greeting: Hello, name: Tim
Hello, Tim
func greeting: Hola, name: Juan
Hola, Juan
func greeting: Hola, name: Taylor
Hola, Taylor

替換捕獲的變量

Go 閉包的強大功能之一是能夠更改捕獲的變量。這使得代碼中的行為更加靈活和動態。下面是一個例子:

func makeCounter() func() int {
 i := 0
 return func() int {
  fmt.Println("func i: ", i)
  i++
  return i
 }
}

func main() {
 counter := makeCounter()
 fmt.Println(counter())
 fmt.Println(counter())
 fmt.Println(counter())
}

在本例中,makeCounter 函數返回一個閉包,每次調用都會使計數器遞增。i 變量被閉包捕獲,并可被修改以更新計數器。

所以其輸出為:

func i:  0
1
func i:  1
2
func i:  2
3

逃逸變量

Go 閉包的另一個高級概念是變量逃逸分析。在 Go 中,變量通常在堆棧上分配,并在超出作用域時被去分配。然而,當變量被閉包捕獲時,它必須在堆上分配,以確保在函數返回后可以訪問它。這會導致性能開銷,因此了解變量何時以及如何逃逸非常重要。

我們對比一下兩個方法:

func makeAdder1(x1 int) func(int) int {
 return func(y1 int) int {
  return x1 + y1
 }
}

func makeAdder2(x2 int) func(int) int {
 fmt.Println(x2)
 return func(y2 int) int {
  return x2 + y2
 }
}

func main() {
 a := makeAdder1(5)
 fmt.Println(a(1))

 b := makeAdder2(6)
 fmt.Println(b(1))
}

makeAdder1 和 makeAdder2 的區別在于函數內的 x 是否被使用。

而我們通過逃逸分析:

go build -gcflags "-m" main.go

會得到以下輸出:

./main.go:5:6: can inline makeAdder1
./main.go:6:9: can inline makeAdder1.func1
./main.go:13:9: can inline makeAdder2.func1
./main.go:12:13: inlining call to fmt.Println
./main.go:19:17: inlining call to makeAdder1
./main.go:6:9: can inline main.makeAdder1.func1
./main.go:20:15: inlining call to main.makeAdder1.func1
./main.go:20:13: inlining call to fmt.Println
./main.go:23:13: inlining call to fmt.Println
./main.go:6:9: func literal escapes to heap
./main.go:12:13: ... argument does not escape
./main.go:12:14: x2 escapes to heap
./main.go:13:9: func literal escapes to heap
./main.go:19:17: func literal does not escape
./main.go:20:13: ... argument does not escape
./main.go:20:15: ~R0 escapes to heap
./main.go:23:13: ... argument does not escape
./main.go:23:15: b(1) escapes to heap

從逃逸分析結果來看,x 變量被閉包捕獲,必須在堆上分配。不過,如果 x 變量不被閉包之外的任何其他代碼使用,編譯器可以進行優化,將其分配到棧中。

共享閉包

最后,Go 中的閉包可以在多個函數之間共享,從而實現更高的靈活性和模塊化代碼。下面是一個例子:

type Calculator struct {
 add func(int, int) int
}

func NewCalculator() *Calculator {
 c := &Calculator{}
 c.add = func(x, y int) int {
  fmt.Printf("func x: %d, y: %d\n", x, y)
  return x + y
 }
 return c
}

func (c *Calculator) Add(x, y int) int {
 return c.add(x, y)
}

func main() {
 calc := NewCalculator()
 fmt.Println(calc.Add(1, 2))
 fmt.Println(calc.Add(2, 3))
}

在本例中,Calculator 結構具有一個 add 函數,該函數在 NewCalculator 函數中通過閉包進行了初始化。Calculator 結構的 Add 方法只需調用 add 函數,這樣就可以在多個上下文中重復使用。

所以其輸出為:

func x: 1, y: 2
3
func x: 2, y: 3
5

結論

在 Go 編程中,閉包是一個強大的工具,可用于用函數封裝數據,并創建生成器和迭代器等。它們提供了一種訪問函數體外定義的變量的方法,即使在函數返回后也是如此。


責任編輯:武曉燕 來源: 愛發白日夢的后端
相關推薦

2012-06-15 14:38:29

Hadoop分布式文件系統

2023-12-25 09:58:25

sync包Go編程

2022-08-08 08:31:55

Go 語言閉包匿名函數

2022-07-19 12:25:29

Go

2022-08-08 06:50:06

Go語言閉包

2023-10-27 07:23:29

WarehouseFlatpak工具

2021-07-13 06:44:04

Go語言數組

2020-08-26 19:15:56

Python工具

2023-11-27 17:03:45

syncGo

2024-04-07 11:33:02

Go逃逸分析

2021-07-15 23:18:48

Go語言并發

2023-12-21 07:09:32

Go語言任務

2024-06-05 14:35:26

2023-11-30 08:09:02

Go語言

2021-06-08 07:45:44

Go語言優化

2023-07-29 15:03:29

2025-04-02 05:23:00

GoChannel數據

2020-12-13 11:38:09

Go語言clac包

2009-12-09 14:38:26

VS.NET 2003

2018-12-19 20:00:52

數字貨幣區塊鏈比特幣
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 91久久久久久久 | 欧美4p | 天天操妹子 | 亚洲精品中文字幕中文字幕 | 久久久婷| 国产精品美女 | 四虎精品在线 | 九九热国产视频 | 国产精品久久久久久久久免费高清 | 久久久爽爽爽美女图片 | 欧美成人免费在线视频 | www.狠狠操| 九九免费观看视频 | 精品久久视频 | 久久精品国产久精国产 | 欧美黑人国产人伦爽爽爽 | av网站观看| 亚洲电影成人 | 一级黄色片免费在线观看 | 国产精品一区二区三区在线 | 亚洲国产成人在线观看 | 蜜月aⅴ免费一区二区三区 99re在线视频 | 我要看一级片 | 天天操天天拍 | 国产精品网页 | 欧美国产中文字幕 | 亚洲网站免费看 | 精品伊人 | 一区二区三区四区在线 | 91久久精品国产91久久性色tv | 国产乱码久久久 | 国产中文字幕在线 | 午夜精品久久久久久久久久久久久 | 日本aaa视频 | 久久99精品久久久久久 | 欧美在线播放一区 | 色吧色综合 | 伊人狠狠干| 久久久国产精品网站 | 青青操91| 男人的天堂一级片 |