日期: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 = 6
func 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) {
return = x * Factorial(x-1)
對比示例一和示例二,哪種看起來更加清晰明白呢?
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,5
i,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)在代碼中應當沒有或者很少令人恐慌的東西。