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