Go語言學習筆記(五) [函數]

日期: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) {
  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,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)在代碼中應當沒有或者很少令人恐慌的東西。
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章