這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。日期:2014年7月29日
1、函數定義:func (p type) funcname(q int) (r,s int) {return 0,0 } func: 保留字,用於定義一個函數 (p type) :可選的,用於定義特定的函數類型,俗稱方法。 funcname:函數名 (q int) :q作為輸入參數,在Go中函數參數是按傳值方式傳遞的。 (r,s int):變數r,s是這個函數的命名傳回值,在Go中函數可以返回多個值,如果不想對返回的參數命名,只需要提供類型:(int, int)。如果只有一個傳回值,可以省略圓括弧。如果函數是一個子過程,並且沒有任何傳回值,也可以省略這些內容 {return 0,0 } :函數體。說明:函數的定義順序可以隨意安排,編譯器會在執行前掃描每個檔案。Go 不允許函數嵌套。然而你可以利用匿名函數實現它。
2、函數遞迴的例子func rec(i int) { if i == 10 { return } rec(i+1) fmt.Printf("%d ",i)}
3、函數的範圍 在 Go 中,定義在函數外的變數是全域的,那些定義在函數內部的變數,對於函數來 說是局部的,局部變數僅僅在執行定義它的函數時有效。如果命名覆蓋——一個局部變數與一個全域變數有相同的名字——在函數執行的時候,局部變數將覆蓋全域變數。 例如: package main
var a = 6func main() { p()
q() p() f() p()}
func p() { print(a)}func q() { a := 5 print(a)}func f() { a = 3 print(a)}
列印結果:65633解釋:函數q()中對a做了定義,此時a的有效範圍就是q()內,而函數f()中,a = 3全域有效a重新賦值了。
當函數調用函數時,範圍又是怎樣的呢?來看下面這個例子:
package main
var a int
func main() { a = 5 print(a) f()}func f() { a := 3 print(a) g()}func g() { print(a)}
列印結果:535解釋: 因為局部變數僅僅在執行定義它的函數時有效,所以g()中的a使用的是外部定義的全域變數。
4、多值返回:和Python一樣,Go也支援函數和方法返回多個值。 例如: func manyvaluereturn(x,y int) (int,int){ return x,x+y } 調用: x,z := manyvaluereturn(4,6) fmt.Printf("x=%d,z=%d",x,z) 執行結果:x=4,z=10
5、命名傳回值 Go 函數的傳回值或者結果參數可以指定一個名字,並且像原始的變數那樣使用,如同輸入參數一般。如果對其命名,在函數開始時,它們會用其類型的零值初始化;如果函數在不加參數的情況下執行了 return 語句,結果參數的當前值會作為傳回值返回。傳回值的名字不是強制的,但是它們可以使得代碼更加健壯和清晰。 例如:
樣本一(沒有命名傳回值):func Factorial(x int) int { if x==0 { return 1 } else { return x * Factorial(x-1) } }樣本一(命名了傳回值):func Factorial(x int) (result int) { if x==0 { return=1 } else { return = x * Factorial(x-1) } return }
對比樣本一和樣本二,哪種看起來更加清晰明白呢?
6、變參:接受不定數量的參數的函數稱為變參函數。例如:func funcname(arg ...int){},arg ...int表示這個函數接受變參,其參數類型全部是int,變數arg是一個int類型的slice。
7、匿名函數與閉包1)匿名函數定義:不需要定義函數名的一種函數實現方式。2)Go語言支援隨時在代碼中定義匿名函數。3)匿名函數可以直接賦值給一個變數或者直接執行例如:package main
import "fmt"
func main() { func(x,y int) { //函數直接執行 fmt.Println(x + y) } (5,6) //這裡的參數列表表示函數調用
f := func(x,y int) int { //匿名函數作為值賦值給f return x * y }
result := f(8,10) fmt.Printf("result=%d",result)}
4)閉包的概念: 閉包是可以包含自由(未綁定到特定對象)變數的代碼塊,這些變數不在這個代碼塊內或者任何全域上下文中定義,而是在定義代碼塊的環境中定義。要執行的代碼塊(由於自由變數包含在代碼塊中,所以這些自由變數以及它們引用的對象沒有被釋放)為自由變數提供綁定的計算環境(範圍)。5)閉包的價值:閉包的價值在於可以作為函數對象或者匿名函數,對於類型系統而言,這意味著不僅要表示 資料還要表示代碼。支援閉包的多數語言都將函數作為第一級對象,就是說這些函數可以儲存到變數中作為參數傳遞給其他函數,最重要的是能夠被函數動態建立和返回。6)Go語言的閉包函數樣本:func closefunc () { var j int = 5 a := func() (func()) { var i int = 10 return func () { fmt.Printf("i,j : %d,%d\n",i,j) } }() a() j *= 2 //修改j的值 a()}執行結果:i,j : 10,5i,j : 10,10
說明:(1)變數a指向的閉包函數引用了局部變數j(closefunc的局部變數)和i(匿名函數的局部變數),i的值被隔離在匿名函數內,在閉包外不能修改。改變j的值,再次調用a,得到的結果是修改後的值。(2)在變數a指向的閉包函數中,只有內部的匿名函數有權訪問變數i,因此保證了i的安全性。
8、函數作為值可以把函數當成值賦值給一個變數。例如:a := func() { //這裡是一個匿名函數 println("Hello!") }
9、錯誤處理1)error介面:Go語言中引入了一個關於錯誤處理的標準模式,即error介面。對於大多數函數,如果要返回錯誤,大致可以定義為如下模式,將error作為多種傳回值的最後一個(並非強制的)。func Foo(param int) (n int ,err error) {}調用是的代碼建議按如下方式處理錯誤情況:n,err := Foo(0) if err != nil { //錯誤處理} else { //使用傳回值n}我們還可以自訂error類型,在此暫不介紹。2)defer(1)一個函數中可以存在多個defer語句,因此需要注意的是,defer語句的調用是遵照先進後出的原則,即最後一個defer語句將最先被執行。3)Panic(恐慌)和Recover(恢複)(1)Go 沒有像 Java 那樣的異常機制:不能拋出一個異常。作為替代,它使用了恐慌和恢 複(panic-and-recover)機制。(2)在代碼中應當沒有或者很少令人恐慌的東西。