Go中的閉包、遞歸

一 閉包詳解閉包的應該都聽過 , 但到底什么是閉包呢?
閉包是由函數及其相關引用環境組合而成的實體(即:閉包=函數+引用環境) 。
“官方”的解釋是:所謂“閉包”,指的是一個擁有許多變量和綁定了這些變量的環境的表達式(通常是一個函數) , 因而這些變量也是該表達式的一部分 。
維基百科講,閉包(Closure) , 是引用了自由變量的函數 。這個被引用的自由變量將和這個函數一同存在,即使已經離開了創造它的環境也不例外 。所以,有另一種說法認為閉包是由函數和與其相關的引用環境組合而成的實體 。閉包在運行時可以有多個實例,不同的引用環境和相同的函數組合可以產生不同的實例 。
看著上面的描述,會發現閉包和匿名函數似乎有些像 ??墒强赡苓€是有些云里霧里的 。因為跳過閉包的創建過程直接理解閉包的定義是非常困難的 。目前在JavaScript、Go、PHP、Scala、Scheme、Common Lisp、Smalltalk、Groovy、Ruby、 Python、Lua、objective c、Swift 以及Java8以上等語言中都能找到對閉包不同程度的支持 。通過支持閉包的語法可以發現一個特點,他們都有垃圾回收(GC)機制 。
在給定函數被多次調用的過程中,這些私有變量能夠保持其持久性 。變量的作用域僅限于包含它們的函數,因此無法從其它程序代碼部分進行訪問 。不過,變量的生存期是可以很長 , 在一次函數調用期間所創建所生成的值在下次函數調用時仍然存在 。正因為這一特點,閉包可以用來完成信息隱藏,并進而應用于需要狀態表達的某些編程范型中 。
二 Go的閉包Go語言是支持閉包的 , 這里只是簡單地講一下在Go語言中閉包是如何實現的 。下面我來將閉包例子用Go來實現 。
package mainimport ("fmt")func a() func() int {i := 0b := func() int {i++fmt.Println(i)return i}return b}func main() {c := a()c()c()c()a() //不會輸出i}輸出結果:
123可以發現,輸出和之前的JavaScript的代碼是一致的 。具體的原因和上面的也是一樣的 , 這說明Go語言是支持閉包的 。
閉包復制的是原對象指針,這就很容易解釋延遲引用現象 。
package mainimport "fmt"func test() func() {x := 100fmt.Printf("x (%p) = %d\n", &x, x)return func() {fmt.Printf("x (%p) = %d\n", &x, x)}}func main() {f := test()f()}【Go中的閉包、遞歸】輸出:
x (0xc42007c008) = 100x (0xc42007c008) = 100在匯編層,test 實際返回的是 FuncVal 對象 , 其中包含了匿名函數地址、閉包對象指針 。當調 匿名函數時,只需以某個寄存器傳遞該對象即可 。
FuncVal { func_address, closure_var_pointer ... }外部引用函數參數局部變量
package mainimport "fmt"http:// 外部引用函數參數局部變量func add(base int) func(int) int {return func(i int) int {base += ireturn base}}func main() {tmp1 := add(10)fmt.Println(tmp1(1), tmp1(2))// 此時tmp1和tmp2不是一個實體了tmp2 := add(100)fmt.Println(tmp2(1), tmp2(2))}返回2個閉包
package mainimport "fmt"http:// 返回2個函數類型的返回值func test01(base int) (func(int) int, func(int) int) {// 定義2個函數,并返回// 相加add := func(i int) int {base += ireturn base}// 相減sub := func(i int) int {base -= ireturn base}// 返回return add, sub}func main() {f1, f2 := test01(10)// base一直是沒有消fmt.Println(f1(1), f2(2))// 此時base是9fmt.Println(f1(3), f2(4))}三 Go 語言遞歸函數遞歸,就是在運行的過程中調用自己 。一個函數調用自己,就叫做遞歸函數 。
構成遞歸需具備的條件:
1.子問題須與原始問題為同樣的事 , 且更為簡單 。2.不能無限制地調用本身 , 須有個出口,化簡為非遞歸狀況處理 。數字階乘一個正整數的階乘(factorial)是所有小于及等于該數的正整數的積,并且0的階乘為1 。自然數n的階乘寫作n! 。1808年,基斯頓·卡曼引進這個表示法 。
package mainimport "fmt"func factorial(i int) int {if i <= 1 {return 1}return i * factorial(i-1)}func main() {var i int = 7fmt.Printf("Factorial of %d is %d\n", i, factorial(i))}輸出結果:
Factorial of 7 is 5040斐波那契數列(Fibonacci)這個數列從第3項開始,每一項都等于前兩項之和 。
package mainimport "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 intfor i = 0; i < 10; i++ {fmt.Printf("%d\n", fibonaci(i))}}輸出結果:
0112358132134

推薦閱讀