Golang 基礎(chǔ)之函數(shù)使用(匿名遞歸閉包)實(shí)例詳解
匿名函數(shù)
在Go語言中,函數(shù)可以像普通變量一樣被傳遞或使用,支持隨時(shí)在代碼里定義匿名函數(shù)。
匿名函數(shù)由一個(gè)不帶函數(shù)名的函數(shù)聲明和函數(shù)體組成。匿名函數(shù)的優(yōu)越性在于可以直接使用函數(shù)內(nèi)的變量,不必申明。
匿名函數(shù)有動(dòng)態(tài)創(chuàng)建的特性,該特性使得匿名函數(shù)不用通過參數(shù)傳遞的方式,就可以直接引用外部的變量。
使用
第一種用法:將匿名函數(shù)賦給變量,在通過變量調(diào)用匿名函數(shù)
sum := func(a, b int) int { return a + b } fmt.Println(sum(1, 2)) // 輸出 3
第二種用法:在定義匿名函數(shù)的時(shí)候直接使用,這種方式只能使用一次傳參
sum := func(a, b int) int { return a + b }(1,2) // 傳入?yún)?shù) fmt.Println(sum) // 輸出 3
第三種用法:將匿名函數(shù)賦給一個(gè)全局變量,匿名函數(shù)在當(dāng)前程序中都可以使用
package main import "fmt" var ( // 全局變量必須首字母大寫 Sum = func(a, b int) int { return a + b } ) func main() { sum := Sum(1, 2) fmt.Println("sum=", sum) // 輸出 sum= 3 }
閉包
所謂“閉包”,指的是一個(gè)擁有許多變量和綁定了這些變量的環(huán)境的表達(dá)式(通常是一個(gè)函數(shù)),因而這些變量也是該表達(dá)式的一部分。
閉包(Closure),是引用了自由變量的函數(shù)。這個(gè)被引用的自由變量將和這個(gè)函數(shù)一同存在,即使已經(jīng)離開了創(chuàng)造它的環(huán)境也不例外。所以,有另一種說法認(rèn)為閉包是由函數(shù)和與其相關(guān)的引用環(huán)境組合而成的實(shí)體。閉包在運(yùn)行時(shí)可以有多個(gè)實(shí)例,不同的引用環(huán)境和相同的函數(shù)組合可以產(chǎn)生不同的實(shí)例。
可以理解為:閉包是匿名函數(shù)與匿名函數(shù)所引用環(huán)境的組合,類似常規(guī)函數(shù)引用全局變量處于一個(gè)包的環(huán)境。
閉包的優(yōu)點(diǎn)
- 變量可以常駐內(nèi)存
- 變量不污染全局
閉包里作用域返回的局部變量不會(huì)被立刻銷毀回收,可能會(huì)占用更多內(nèi)存過度使用閉包會(huì)導(dǎo)致性能下降。
使用
package main import "fmt" func main() { n := 0 count := func() int { // 這就是一個(gè)閉包 n += 1 return n } fmt.Println(count()) // 輸出 1 fmt.Println(count()) // 輸出 2 }
常規(guī)函數(shù)、匿名函數(shù) + 全局變量 + 包就等同于閉包, count
不僅存儲(chǔ)了函數(shù)的返回值,還存儲(chǔ)了閉包的狀態(tài)。
閉包被返回賦予一個(gè)同類型的變量時(shí),同時(shí)賦值的是整個(gè)閉包的狀態(tài),該狀態(tài)會(huì)一直存在外部被賦值的變量count
中,直到count
被銷毀,整個(gè)閉包生命周期結(jié)束。
也可以寫成下列形式
package main import "fmt" func Count() func() int { // 返回函數(shù) n := 0 return func() int { n++ return n } } func main() { count := Count() fmt.Println(count()) // 輸出 1 fmt.Println(count()) // 輸出 2 }
高級(jí)閉包特性,比如并發(fā)中的閉包。 將放到后面章節(jié)為大家介紹。
遞歸函數(shù)
遞歸,就是在運(yùn)行的過程中調(diào)用自己。
一個(gè)函數(shù)調(diào)用自己, 就叫做遞歸函數(shù)。
構(gòu)成遞歸具備的條件:
- 子問題需要與原始問題為同樣的事,且更為簡單。
- 不能無限制地調(diào)用本身,須有個(gè)出口,化簡為非遞歸狀況處理。
使用
舉例:數(shù)字階乘
一個(gè)正整數(shù)的階乘是所有小于及等于該數(shù)的正整數(shù)的積,并且0的階乘為1。
package main import "fmt" func factorial(i int) int { // 解讀為 5*4*3*2*1=120 if i <= 1 { return 1 } return i * factorial(i-1) } func main() { var i int = 5 fmt.Printf("%d\n", factorial((i))) // 120 }
1808年,基斯頓·卡曼引進(jìn)這個(gè)表示法。
舉例:裴波那契數(shù)列(Fibonacci)
這個(gè)數(shù)列從第3項(xiàng)開始,每一項(xiàng)都等于前兩項(xiàng)之和。
package main import "fmt" func fibonaci(i int) int { if i == 0 { return 0 } if i == 1 { return 1 } return fibonaci(i-1) + fibonaci(i-2) } func main() { var i int for i = 0; i < 10; i++ { fmt.Printf("%d ", fibonaci(i)) // 0 1 1 2 3 5 8 13 21 34 } }
延遲調(diào)用 (defer)
在基礎(chǔ)語法中已經(jīng)介紹了defer延遲調(diào)用的使用,今天深入了解使用一下defer機(jī)制。
defer特性
- 關(guān)鍵字 defer 用于注冊(cè)延遲調(diào)用。
- 這些調(diào)用直到 return 前才被執(zhí)。因此,可以用來做資源清理。
- 多個(gè)defer語句,按先進(jìn)后出的方式執(zhí)行。
- defer語句中的變量,在defer聲明時(shí)就決定了。
- 某個(gè)延遲調(diào)用發(fā)生錯(cuò)誤,這些調(diào)用依舊會(huì)被執(zhí)行。
defer后面的語句在執(zhí)行的時(shí)候,函數(shù)調(diào)用的參數(shù)會(huì)被保存起來,但是不執(zhí)行。也就是復(fù)制了一份。
defer用途
- 關(guān)閉文件句柄
- 鎖資源釋放
- 數(shù)據(jù)庫連接釋放
使用
多個(gè) defer 注冊(cè),按 FILO 次序執(zhí)行 ( 先進(jìn)后出 ) 原則。
package main func main() { defer println("1") // 先進(jìn)來, 最后出去 defer println("2") defer println("3") // 最后進(jìn)來, 先出去 }
輸出
3
2
1
延遲調(diào)用參數(shù)在注冊(cè)時(shí)求值或復(fù)制,可以用指針或閉包 “延遲” 讀取。
package main import "fmt" func main() { x, y := 10, 100 defer func(i int) { fmt.Printf("defer x = %v, y = %v\n", i, y) // y 閉包引用 }(x) // x 被復(fù)制 x += 10 y += 20 println("x = ", x, "y = ", y) }
輸出
x = 20 y = 120
defer x = 10, y = 120
defer和return兩者執(zhí)行順序
- 有名返回值 (函數(shù)返回值為已經(jīng)命名的返回值)
package main import "fmt" func foo() (i int) { // 3.return i 值 i = 0 defer func() { fmt.Println(i) // 2.讀取臨時(shí)變量地址(返回值) }() return 2 // 1.返回值賦值,寫入臨時(shí)變量 } func main() { foo() }
輸出
2
在 foo()
返回值的函數(shù)中 (這里返回值為 i),執(zhí)行 return 2
的時(shí)候?qū)嶋H上已經(jīng)將i 的值重新賦值為2。 所以defer closure輸出結(jié)果為2。
解析:
return 的機(jī)制:1.首先將返回值放到一個(gè)臨時(shí)變量中(為返回值賦值) 2. 然后將返回值返回到被調(diào)用處。
而defer函數(shù)恰在return的兩個(gè)操作之間執(zhí)行。
執(zhí)行順序是: 先為返回值賦值,即將返回值放到一個(gè)臨時(shí)變量中,然后執(zhí)行defer,然后return到函數(shù)被調(diào)用處。
- 無名返回值 (即函數(shù)返回值為沒有命名的函數(shù)返回值)
package main import "fmt" func foo() int { var i int defer func() { i++ // 這個(gè)地方 i,不是臨時(shí)變量 fmt.Println("defer = ", i) // 輸出 defer = 1 }() return i // 返回值賦值,寫入臨時(shí)變量 } func main() { fmt.Println("return = ", foo()) // 輸出 return = 0 }
解析:return 先把返回值放到一個(gè)臨時(shí)變量中,defer函數(shù)無法獲取到這個(gè)臨時(shí)變量地址 (沒有函數(shù)返回值),所以無論defer函數(shù)做任何操作,都不會(huì)對(duì)最終返回值造成任何變動(dòng)。
以上就是Golang 基礎(chǔ)之函數(shù)使用(匿名遞歸閉包)實(shí)例詳解的詳細(xì)內(nèi)容,更多關(guān)于Golang 匿名遞歸函數(shù)閉包的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
搭建Go語言的ORM框架Gorm的具體步驟(從Java到go)
很多朋友不知道如何使用Goland軟件,搭建一個(gè)ORM框架GORM,今天小編給大家分享一篇教程關(guān)于搭建Go語言的ORM框架Gorm的具體步驟(從Java到go),感興趣的朋友跟隨小編一起學(xué)習(xí)下吧2022-09-09go語言數(shù)組及結(jié)構(gòu)體繼承和初始化示例解析
這篇文章主要為大家介紹了go語言數(shù)組及結(jié)構(gòu)體繼承和初始化示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-04-04golang通過mysql語句實(shí)現(xiàn)分頁查詢
這篇文章主要介紹了golang通過mysql語句實(shí)現(xiàn)分頁查詢,文章內(nèi)容介紹詳細(xì),具有一定的參考價(jià)值,需要的小伙伴可以參考一下,希望對(duì)你的學(xué)習(xí)有所幫助2022-03-03Golang高性能持久化解決方案BoltDB數(shù)據(jù)庫介紹
這篇文章主要為大家介紹了Golang高性能持久化解決方案BoltDB數(shù)據(jù)庫介紹,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2021-11-11win7下配置GO語言環(huán)境 + eclipse配置GO開發(fā)
這篇文章主要介紹了win7下配置GO語言環(huán)境 + eclipse配置GO開發(fā),需要的朋友可以參考下2014-10-10