Go中的函數一些有趣的功能

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

Go中的函數

函數是Go裡面的核心設計,它通過關鍵字func來申明,他的格式如下
func funcname(input1 type1, input2 type2) (output1 type1, output2 type2) {
         //這裡是處理邏輯代碼
         //返回多個值
         return value1, value2
}

函數有以下特徵:

  • 關鍵字func用來申明一個函數funcname,匿名函數可以沒有funcname。
  • 函數可以有一個或者多個參數,每個參數後面帶有類型,通過,分隔
  • 函數可以返回多個值
  • 上面傳回值申明了兩個變數output1和output2,如果你不想申明也可以,直接就兩個類型
  • 如果只有一個傳回值且不申明傳回值變數,那麼你可以省略用以包括傳回值的括弧
  • 如果沒有傳回值,那麼就直接省略最後的返回資訊

 

函數作為值、類型

在Go中函數也是一種變數,我們可以通過type來定義他,他的類型就是所有擁有相同的參數,相同的傳回值的一種類型 type type_name func(input1 inputType1 [, input2 inputType2 [, ...]) (result1 resultType1 [, ...])

函數作為類型到底有什麼好處呢?那就是可以把這個類型的函數當做值來傳遞,請看下面的例子。

package main
import "fmt"

type test_int func(int) bool //申明了一個函數類型

func isOdd(integer int) bool {
        if integer%2 == 0 {
               return false
       }
       return true
}

func isEven(integer int) bool {
       if integer%2 == 0 {
              return true
       }
       return false
}

//申明的函數類型在這個地方當做了一個參數
func filter(slice []int, f test_int) []int {
       var result []int
       for _, value := range slice {
              if f(value) {
                     result = append(result, value)
              }
       }
       return result
}

func main(){
       slice := []int {1, 2, 3, 4, 5, 7}
       fmt.Println("slice = ", slice)
       odd := filter(slice, isOdd) //函數當做值來傳遞了
       fmt.Println("Odd elements of slice are: ", odd)
       even := filter(slice, isEven)//函數當做值來傳遞了
       fmt.Println("Even elements of slice are: ", even)
}

函數當做值和類型在我們寫一些通用介面的時候非常有用,通過上面例子我們看到test_int這個類型是一個函數類型,然後兩個filter函數的參數和傳回值與test_int類型是一樣的,但是我們可以實現很多種的邏輯,這樣使得我們的程式變得非常的靈活。

函數傳回值是函數的情況

技術參考: http://www.cnblogs.com/cool-xing/archive/2012/05/19/2509176.html

假如你擁有一份吃過壽司的人的清單, 你是否能夠根據人名確定他是否在清單上? 這是個很簡單的問題, 你只需遍曆清單. 嗯, 如果你go的功底很弱, 不知道怎麼遍曆清單那怎麼辦? 沒關係, 我會給你提供一個刷選器:

func Screen(patients []string) func(string) bool {
         // 定義匿名函數並返回
         return func(name string) bool {
                  for _, soul := range patients {
                           if soul == name {
                                    return true
                           }
                  }
                  return false
         }
}

Screen方法會將刷選的函數返回給調用方, 這樣你就可以不用懂怎麼去遍曆清單了, 你只需調用我返回給你的函數就可以:

// 吃過壽司的人的清單
those_who_bought_sushi := []string{"Anand", "JoJo", "Jin", "Mon", "Peter", "Sachin"}
// 得到刷選器函數
bought_sushi := Screen(those_who_bought_sushi)
// 調用刷選器函數就可以知道某人是否在清單上
fmt.Println(bought_sushi("Anand")) // true
fmt.Println(bought_sushi("Alex")) // false

閉包

地球人都知道:函數只是一段可執行代碼,編譯後就“固化”了,每個函數在記憶體中只有一份執行個體,得到函數的進入點便可以執行函數了。go語言中函數可以作為另一個函數的參數或傳回值,可以賦給一個變數。函數可以嵌套定義(使用匿名函數),即在一個函數內部可以定義另一個函數,有了嵌套函數這種結構,便會產生閉包問題。如:

package main
import "fmt"

func ExFunc(n int) func() {
         sum:=n
         return func () { //把匿名函數作為值賦給變數a (Go 不允許函數嵌套。
                                   //然而你可以利用匿名函數實現函數嵌套)
                  fmt.Println(sum+1) //調用本函數外的變數
         } //這裡沒有()匿名函數不會馬上執行
}

func main() {
         myFunc:=ExFunc(10)
         myFunc()     // 11
         myAnotherFunc:=ExFunc(20)
         myAnotherFunc()    //21
         myFunc()       //11
         myAnotherFunc()   //21
}

這裡執行結果:

11


21


11


21

在這段程式中,匿名函數是函數ExFunc的內嵌函數,並且是ExFunc函數的傳回值。我們注意到一個問題:這裡的匿名內嵌函數中引用到外層函數中的局部變數sum,Go會這麼處理這個問題呢?先讓我們來看看這段代碼的運行結果。當我們調用分別由不同的參數調用ExFunc函數得到的函數時(myFunc(),myAnotherFunc()),得到的結果是隔離的,也就是說每次調用ExFunc函數後都將產生並儲存一個新的局部變數sum。其實這裡ExFunc函數返回的就是閉包。

 

按照命令式語言的規則,ExFunc函數只是返回了內嵌函數InsFunc的地址,在執行InsFunc函數時將會由於在其範圍內找不到sum變數而出錯。而在函數式語言中,當內嵌函數體內引用到體外的變數時,將會把定義時涉及到的引用環境和函數體打包成一個整體(閉包)返回。現在給出引用環境的定義就容易理解了:引用環境是指在程式執行中的某個點所有處於活躍狀態的約束(一個變數的名字和其所代表的對象之間的聯絡)所組成的集合。閉包的使用和正常的函數調用沒有區別。

 

由於閉包把函數和運行時的引用環境打包成為一個新的整體,所以就解決了函數編程中的嵌套所引發的問題。如上述程式碼片段中,當每次調用ExFunc函數時都將返回一個新的閉包執行個體,這些執行個體之間是隔離的,分別包含調用時不同的引用環境現場。不同於函數,閉包在運行時可以有多個執行個體,不同的引用環境和相同的函數組合可以產生不同的執行個體。

閉包函數是把建立時,引用到的外部資料複製了一份,與函數一起組成了一個整體。

閉包函數出現的條件:
1.被嵌套的函數引用到非本函數的外部資料,而且這外部資料不是“全域變數”
2.函數被獨立了出來(被父函數返回或賦值給其它函數或變數了)

回來看閉包的定義:閉包是什麼,閉包是由函數及其相關的引用環境組合而成的實體(即:閉包=函數+引用環境)。

對象是附有行為的資料,而閉包是附有資料的行為

參考: http://www.cnblogs.com/Jifangliang/archive/2008/08/05/1260602.html

http://blog.sina.com.cn/s/blog_487109d101018fcx.html

package main
 
import "fmt"
 
 
func ExFunc(n int) func() {
    sum := n
    a := func() {
        sum++        //在這裡對外部資料加1
        fmt.Println(sum)
    }
    return a
}
 
func main() {
    myFunc := ExFunc(10)
    myFunc()
    myAnotherFunc := ExFunc(20)
    myAnotherFunc()
 
    myFunc()
    myAnotherFunc()     //這裡得出的結果是22,由此可以證明兩點
                        //1.閉包中對外部資料的修改,外部不可見
                        //2.外部資料的值被儲存到建立的靜態變數中
                    
}
 
 

實驗

看下面幾種情況,對比執行結果

package main
 
import"fmt"
 
func main(){
    var j int=5
 
    a:=func()func(){
        var i int=10
        fmt.Printf("\neeee:%d\n",j)
        return func(){
            fmt.Printf("i,j:%d,%d\n",i,j)
        }
    }()
 
    a()
    j*=2
    a()
}
執行結果:

eeee:5

i,j:10,5

i,j:10,10

exit code 0, process exited normally.



例子二

package main
 
import"fmt"
 
func main(){
    var j int=5
 
    a:=func()func(){
        var i int=10
        fmt.Printf("\neeee:%d\n",j)
        return func(){
            fmt.Printf("i,j:%d,%d\n",i,j)
        }
    }
 
    a()
    j*=2
    a()
}
執行結果:

eeee:5

eeee:10
exit code 0, process exited normally.

例子三

package main
 
import"fmt"
 
func main(){
    var j int=5
 
    a:=func() func(){
        var i int=10
        fmt.Printf("\neeee:%d\n",j)
        return func(){
            fmt.Printf("i,j:%d,%d\n",i,j)
        }
    }
 
    a()()
    j*=2
    a()()
}
執行結果:

eeee:5
i,j:10,5

eeee:10
i,j:10,10
exit code 0, process exited normally.

 



 

參考資料:

https://github.com/astaxie/build-web-application-with-golang/blob/master/02.3.md

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.