golang 碎片整理之 函數

函數是組織好的,可重複利用的、用於執行指定任務的代碼塊。
go語言中支持函數、匿名函數和閉包,而且函數在go語言中屬於「一等公民」。golang

函數定義

go語言中使用func關鍵字定義函數,具體格式以下:markdown

func 函數名(參數) (返回值){
    函數體
}

其中,閉包

  • 函數名由字母、數字、下劃線組成,可是函數名的第一個字母不能是數字,在同一個包內,函數名也不能重名。
  • 參數:參數由參數變量和參數變量的類型組成,多個參數之間使用,分隔。
  • 返回值:返回值由返回值變量和其變量類型組成,也能夠只寫返回值的類型,多個返回值必須用()包裹,並用,分隔。
  • 函數體:實現指定功能的代碼塊

咱們先來定義一個求兩個數之和的函數ide

func sumint(x int, y int) int {
     return x+y
}

函數的參數和返回值都是可選的,例如咱們能夠實現一個既不須要參數也沒有返回值的函數:函數

func sayHello() {
    fmt.Println("Hello 沙河")
}

函數的調用

定義了函數後,咱們經過 函數名() 的方式來調用函數,例如咱們調用上面定義的函數:code

func main() {
    sayHello()
    ret := intSum(10, 20)
    fmt.Println(ret)
}

注意,調用有返回值的函數時,能夠不接收其返回值。資源

參數

類型簡寫

函數的參數中,若是相鄰變量的類型相同,能夠省略類型,以下:作用域

func intSum(x, y int) int {
    return x + y
}

上面的代碼,函數有兩個參數,這兩個參數的類型均爲int,所以能夠省略x的類型,由於y後面有類型說明,x參數也是該類型。it

可變參數

可變參數是指函數的參數數量不固定,Go語言中的可變參數,經過在參數名後面加...來標識。
注意:可變參數一般做爲函數的最後一個參數。
舉個例子:for循環

func intSum2(x ...int) int {
    fmt.Println(x) //x是一個切片
    sum := 0
    for _, v := range x {
        sum = sum + v
    }
    return sum
}

返回值

go語言中經過return關鍵字向外輸出函數的返回值。

多返回值

go語言中的函數支持多個返回值,並在函數體中直接使用這些變量,最後經過return 關鍵詞返回。

func calc(x, y int) (sum, sub int) {
    sum = x + y
    sub = x - y
    return
}

defer語句

因爲go語句中的延遲調研的特性,因此defer語句能很是方便的處理資源釋放問題,好比:資源清理、文件關閉、解鎖及記錄時間等。

defer 的執行時機和案例分析

在Go語言的函數中return語句在底層並非原子操做,它分爲給返回值賦值和RET指令兩步。而defer語句執行的時機就在返回值賦值操做後,RET指令執行前。具體以下圖所示:

golang 碎片整理之 函數

func f1() int {
    x := 5
    defer func() {
        x++
    }()
    return x
}

func f2() (x int) {
    defer func() {
        x++
    }()
    return 5
}

func f3() (y int) {
    x := 5
    defer func() {
        x++
    }()
    return x
}
func f4() (x int) {
    defer func(x int) {
        x++
    }(x)
    return 5
}
func main() {
    fmt.Println(f1())
    fmt.Println(f2())
    fmt.Println(f3())
    fmt.Println(f4())
}

函數進階

變量做用域

全局變量

全局變量是定義在函數外的變量,它在程序整個運行週期內都有效。在函數中能夠訪問到全局變量。

package main
import (
    "fmt"
)
var num int = 10
func testGlobal(){
    fmt.Printf("num =%d\n", num)
}
func main(){
    testGlobal()
}

局部變量

局部變量又分爲兩種:函數內定義的變量沒法再該函數外使用,例以下面的示例代碼main函數中沒法使用testLocalvar函數中定義的變量x:

package main
import (
    "fmt"
)
func testLocalvar(){
    var x int = 100
    fmt.Printf("x=%d\n",x)
}
func main(){
    testLocalvar()
    fmt.Println(x)
}

若是局部變量和全局變量重名,優先訪問局部變量:

package main
import (
    "fmt"
)
var num int = 100
func testNum(){
    num := 100
    fmt.Printf("num=%d\n",num)

}
func main(){
    testNum() //num =100
}

接下來咱們來看一下語句塊定義的變量,一般咱們會在if條件判斷、for循環、switch語句上使用這種定義變量的方式。

package main
import (
    "fmt"
)
func testLocalvar(x,y int){
    fmt.Println(x,y)
    if x > 0 {
        z := 100
        fmt.Println(z)
    }
    fmt.Println(z) //此處沒法使用變量i

}
func main(){
    testLocalvar(1,2) //num =100
}

函數做爲變量

函數能夠做爲變量進行傳遞:

func main() {
    f1 := add                         //將函數add賦值給變量f1
    fmt.Printf("type of f1:%T\n", f1) //type of f1:func(int, int) int
    ret := f1(10, 20)
    fmt.Println(ret)
}

函數能夠做爲參數

func add(x, y int) int {
return x + y
}
func calc(x, y int, op func(int, int) int) int {
return op(x, y)
}
func main() {
ret2 := calc(10, 20, add)
fmt.Println(ret2) //30
}

匿名函數

函數還能夠做爲返回值,可是在Go語言中函數內部不能再像以前那樣定義函數了,只能定義匿名函數,匿名函數就是沒有函數名的函數,匿名函數的定義格式以下:

func(參數)(返回值){
    函數體
}

匿名函數由於沒有函數名,因此沒辦法像普通函數那樣調用,全部匿名函數須要保存到某個變量或者做爲當即執行函數:

package main
import (
    "fmt"
)
func main(){
    add := func(x,y int){
        fmt.Println(x+y)
    }
    add(10,20)
    func(x,y int){
        fmt.Println(x+y)
    }(10,20)
}

閉包

閉包指的是一個函數和與其相關引用環境組合而成的實體。簡單的說,閉包=函數+引用環境。首先咱們看一個實例:

func adder() func(int) int {
    var x int
    return func(y int) int {
        x += y
        return x
    }
}
func main() {
    var f = adder()
    fmt.Println(f(10)) //10
    fmt.Println(f(20)) //30
    fmt.Println(f(30)) //60

    f1 := adder()
    fmt.Println(f1(40)) //40
    fmt.Println(f1(50)) //90
}
相關文章
相關標籤/搜索