1. 程式人生 > >十一、Go基礎程式設計:遞迴函式、函式型別、匿名函式與閉包

十一、Go基礎程式設計:遞迴函式、函式型別、匿名函式與閉包

1. 遞迴函式

遞迴指函式可以直接或間接的呼叫自身。

遞迴函式通常有相同的結構:一個跳出條件和一個遞迴體。所謂跳出條件就是根據傳入的引數判斷是否需要停止遞迴,而遞迴體則是函式自身所做的一些處理。

//通過迴圈實現1+2+3……+100
func Test01() int {
    i := 1
    sum := 0
    for i = 1; i <= 100; i++ {
        sum += i
    }

    return sum
}

//通過遞迴實現1+2+3……+100
func Test02(num int) int {
    if num == 1 {
        return 1
    }

    return num + Test02(num-1) //函式呼叫本身
}

//通過遞迴實現1+2+3……+100
func Test03(num int) int {
    if num == 100 {
        return 100
    }

    return num + Test03(num+1) //函式呼叫本身
}

func main() {

    fmt.Println(Test01())    //5050
    fmt.Println(Test02(100)) //5050
    fmt.Println(Test03(1))   //5050
}

2. 函式型別

在Go語言中,函式也是一種資料型別,我們可以通過type來定義它,它的型別就是所有擁有相同的引數,相同的返回值的一種型別。

type FuncType func(int, int) int //宣告一個函式型別, func後面沒有函式名

//函式中有一個引數型別為函式型別:f FuncType
func Calc(a, b int, f FuncType) (result int) {
    result = f(a, b) //通過呼叫f()實現任務
    return
}

func Add(a, b int) int {
    return a + b
}

func Minus(a, b int) int {
    return a - b
}

func main() {
    //函式呼叫,第三個引數為函式名字,此函式的引數,返回值必須和FuncType型別一致
    result := Calc(1, 1, Add)
    fmt.Println(result) //2

    var f FuncType = Minus
    fmt.Println("result = ", f(10, 2)) //result =  8
}

3. 匿名函式與閉包

所謂閉包就是一個函式“捕獲”了和它在同一作用域的其它常量和變數。這就意味著當閉包被呼叫的時候,不管在程式什麼地方呼叫,閉包能夠使用這些常量或者變數。它不關心這些捕獲了的變數和常量是否已經超出了作用域,所以只有閉包還在使用它,這些變數就還會存在。

在Go語言裡,所有的匿名函式(Go語言規範中稱之為函式字面量)都是閉包。匿名函式是指不需要定義函式名的一種函式實現方式,它並不是一個新概念,最早可以回溯到1958年的Lisp語言。
 

func main() {
    i := 0
    str := "mike"

    //方式1
    f1 := func() { //匿名函式,無參無返回值
        //引用到函式外的變數
        fmt.Printf("方式1:i = %d, str = %s\n", i, str)
    }

    f1() //函式呼叫

    //方式1的另一種方式
    type FuncType func() //宣告函式型別, 無參無返回值
    var f2 FuncType = f1
    f2() //函式呼叫

    //方式2
    var f3 FuncType = func() {
        fmt.Printf("方式2:i = %d, str = %s\n", i, str)
    }
    f3() //函式呼叫

    //方式3
    func() { //匿名函式,無參無返回值
        fmt.Printf("方式3:i = %d, str = %s\n", i, str)
    }() //別忘了後面的(), ()的作用是,此處直接呼叫此匿名函式

    //方式4, 匿名函式,有參有返回值
    v := func(a, b int) (result int) {
        result = a + b
        return
    }(1, 1) //別忘了後面的(1, 1), (1, 1)的作用是,此處直接呼叫此匿名函式, 並傳參
    fmt.Println("v = ", v)

}

閉包捕獲外部變數特點:

func main() {
    i := 10
    str := "mike"

    func() {
        i = 100
        str = "go"
        //內部:i = 100, str = go
        fmt.Printf("內部:i = %d, str = %s\n", i, str)
    }() //別忘了後面的(), ()的作用是,此處直接呼叫此匿名函式

    //外部:i = 100, str = go
    fmt.Printf("外部:i = %d, str = %s\n", i, str)
}

函式返回值為匿名函式: 

// squares返回一個匿名函式,func() int
// 該匿名函式每次被呼叫時都會返回下一個數的平方。
func squares() func() int {
    var x int
    return func() int {//匿名函式
        x++ //捕獲外部變數
        return x * x
    }
}

func main() {
    f := squares()
    fmt.Println(f()) // "1"
    fmt.Println(f()) // "4"
    fmt.Println(f()) // "9"
    fmt.Println(f()) // "16"
}

函式squares返回另一個型別為 func() int 的函式。對squares的一次呼叫會生成一個區域性變數x並返回一個匿名函式。每次呼叫時匿名函式時,該函式都會先使x的值加1,再返回x的平方。第二次呼叫squares時,會生成第二個x變數,並返回一個新的匿名函式。新匿名函式操作的是第二個x變數。