<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Go語言中的閉包:封裝數(shù)據(jù)與功能的強(qiáng)大工具

          共 6186字,需瀏覽 13分鐘

           ·

          2023-11-08 20:00

          來源: 愛發(fā)白日夢(mèng)的后端


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

          什么是閉包?

          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.

          翻譯過來就是:

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

          閉包是一種創(chuàng)建函數(shù)的方法,這些函數(shù)可以訪問在其主體之外定義的變量。閉包是一個(gè)可以捕捉其周圍環(huán)境狀態(tài)的函數(shù)。這意味著函數(shù)可以訪問不在其參數(shù)列表中或在其主體中定義的變量。閉包函數(shù)可以在外部函數(shù)返回后訪問這些變量。

          在 Go 中創(chuàng)建閉包

          在 Go 中,您可以使用匿名函數(shù)創(chuàng)建閉包。創(chuàng)建閉包時(shí),函數(shù)會(huì)捕獲其周圍環(huán)境的狀態(tài),包括外部函數(shù)中定義的任何變量。閉包函數(shù)可以在外部函數(shù)返回后訪問這些變量。

          下面是一個(gè)在 Go 中創(chuàng)建閉包的示例:

          func adder() func(int) int { // 外部函數(shù)
           sum := 0
           return func(x int) int { // 內(nèi)部函數(shù)
            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))
          }

          在本例中,我們定義了一個(gè)返回匿名函數(shù)的加法器函數(shù)。匿名函數(shù)捕捉加法器函數(shù)中定義的 sum 變量的狀態(tài)。每次調(diào)用匿名函數(shù)時(shí),它都會(huì)將參數(shù)加到求和變量中,并返回結(jié)果。

          所以其輸出結(jié)果為:

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

          在 Go 中使用閉包

          在 Go 中,閉包可用于多種用途,包括用函數(shù)封裝數(shù)據(jù)、創(chuàng)建生成器、迭代器和 memoization 函數(shù)。

          下面是一個(gè)使用閉包將數(shù)據(jù)與函數(shù)封裝在一起的示例:

          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"))
          }

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

          所以其輸出為:

          func greetingHellonameJohn
          Hello, John
          func greetingHellonameTim
          Hello, Tim
          func greetingHolanameJuan
          Hola, Juan
          func greetingHolanameTaylor
          Hola, Taylor

          替換捕獲的變量

          Go 閉包的強(qiáng)大功能之一是能夠更改捕獲的變量。這使得代碼中的行為更加靈活和動(dòng)態(tài)。下面是一個(gè)例子:

          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 函數(shù)返回一個(gè)閉包,每次調(diào)用都會(huì)使計(jì)數(shù)器遞增。i 變量被閉包捕獲,并可被修改以更新計(jì)數(shù)器。

          所以其輸出為:

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

          逃逸變量

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

          我們對(duì)比一下兩個(gè)方法:

          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))
          }

          makeAdder1makeAdder2 的區(qū)別在于函數(shù)內(nèi)的 x 是否被使用。

          而我們通過逃逸分析:

          go build -gcflags "-m" main.go

          會(huì)得到以下輸出:

          ./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:9func literal escapes to heap
          ./main.go:12:13: ... argument does not escape
          ./main.go:12:14: x2 escapes to heap
          ./main.go:13:9func literal escapes to heap
          ./main.go:19:17func 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

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

          共享閉包

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

          type Calculator struct {
           add func(intint) 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(12))
           fmt.Println(calc.Add(23))
          }

          在本例中,Calculator 結(jié)構(gòu)具有一個(gè) add 函數(shù),該函數(shù)在 NewCalculator 函數(shù)中通過閉包進(jìn)行了初始化。Calculator 結(jié)構(gòu)的 Add 方法只需調(diào)用 add 函數(shù),這樣就可以在多個(gè)上下文中重復(fù)使用。

          所以其輸出為:

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

          結(jié)論

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

          GopherChina 2023上海站 

          快點(diǎn)擊閱讀原文報(bào)名吧~~

          瀏覽 1627
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲天堂在线电影播放 | 爱爱爱视频免费 | www.xxxx日本免费 | 午夜成人在线小电影 麻豆 | 操逼大秀 |