Golang 函數function


函數function

  • Go函數不支持嵌套、重載和默認參數
  • 但支持以下特性:

  1. 無需聲明原型
  2. 不定長度變參
  3. 多返回值
  4. 命名返回值參數
  5. 匿名函數
  6. 閉包
  • 定義函數使用關鍵字func,且左大括號不能另起一行
  • 函數也可以作為一種類型使用

返回值及參數說明

func A(a int, b string) (int, string, int)  { //第一個小括號當中是你的參數列表,第二個小括號是你的返回值列表
	
}
func A(a, b, c int) (int, string, int)  {
	//如果abc都是int型的話,可以按照這種方法進行簡寫,同樣的方法也適用於返回值當中

}
func A() (a, b, c int)  { //1:如果這樣寫的話就必須要命名返回值
	//命名返回值和不命名返回值得區別
}
func A() (int, int, int)  { //
	//命名返回值和不命名返回值得區別
	a, b, c := 1,2,3
	return a,b,c
	//如果此時沒有命名返回值的時候,那么在返回值得時候就必須寫上return a,b,c
	//當然為了代碼的可讀性,這里我們規定必須return 的時候加上返回值名
}

不定長變參

package main

import "fmt"

func main()  {
	A(1,2,3,4,5,6,7)


}

func A(a ...int) {
	// 這里采用的是不定長變參,不定長變參必須是參數的最后一個參數,后面不能再跟 b string這樣的參數
	fmt.Println(a)
}
package main

import "fmt"

func main()  {
	s1:= []int{1,2,3,4}
	a,b :=1,2
	A(a,b)
	fmt.Println(a,b)
	B(s1)
	fmt.Println(s1)


}

func A(a ...int) {
	//這里傳進來的實際上是一個slice,引用類型
	a[0] = 3
	a[1] = 4
	//盡管我們在函數A當中接收到的是一個slice,但是它得到的是一個值拷貝
	//和直接傳遞一個slice的區別看函數B
	fmt.Println(a)
}
func B(s []int)  {
	//這里並不是傳遞一個指針進去,而是對這個slice的內存地址進行了一個拷貝
	//這里還可以看到像int型、string型進行常規的參數傳進去的話,只是進行了個值拷貝,slice傳進去雖然也是拷貝,但是它是內存地址的拷貝
	s[0] = 4
	s[1] = 5
	s[2] = 6
	s[3] = 7
	fmt.Println(s)
	//在這里 我們看到我們在函數B當中的修改,實際上影響到了我們main函數當中的變量s1
	//如果直接傳遞一個slice,它的修改就會影響到這個slice的本身

}

PS:值類型和引用類型進行函數傳參拷貝是不一樣的,一個是拷貝值,一個是拷貝地址
package main

import (
	"fmt"
)

func main() {
	a := 1
	A(&a) //這里取出a的地址
	fmt.Println(a)

}

func A(a *int) { //傳遞的是指針類型
	*a = 2 //在操作的時候需要去它的值進行操作,這個時候函數A就可以改變原始a的值
	fmt.Println(*a)
}

函數類型的使用

package main

import (
	"fmt"
)

func main() {
	a := A
	a() //這個時候是將A的函數類型賦值給a,在go語言當中一切皆是類型啊

}
func A() {
	fmt.Println("Func A")
}

匿名函數的使用

package main

import (
	"fmt"
)

func main() {
	a := func() {
		//此時這個代碼塊就是一個匿名函數,這個函數本身沒有名稱,我們將她賦值給a,然后調用
		fmt.Println("Func A")

	}
	a() //依然可以打印func A
}

GO語言當中的閉包

package main

import (
	"fmt"
)

func main() {
	f := closure(10)
	res1 := f(1)
	fmt.Println(res1)
	res2 := f(2)
	fmt.Println(res2)

}

func closure(x int) func(int) int {
	fmt.Printf("%p \n", &x)
	return func(y int) int {
		fmt.Printf("%p \n", &x)
		return x + y
	}
}
//這里可以看出3次打印x的地址都是一樣的

defer

  • defer的執行方式類似其它語言中的析構函數,在函數執行體結束后按照調用順序的相反順序逐個執行

  • 即使函數發生嚴重錯誤也會執行

  • 支持匿名函數的調用

  • 通常用於資源清理、文件關閉、解鎖以及記錄時間等操作

  • 通過與匿名函數配合可在return之后修改函數計算結果

  • 如果函數體內某個變量作為defer時匿名函數的參數,則在定義defer時即已經獲得了拷貝,否則則是引用某個變量的地址

  • GO沒有異常機制,但有panic/recove模式來處理錯誤

  • Panic可以在任何地方引發,但recover只有在defer調用的函數中有效

package main

import (
	"fmt"
)

func main() {
	fmt.Println("A")
	defer fmt.Println("B")
	defer fmt.Println("C")
}
//PS:打印的結果就是A C B
package main

import (
	"fmt"
)

func main() {
	for i := 0; i < 3; i++ {
		//defer fmt.Println(i)
		defer func() {
			fmt.Println(i)
		}() //調用這個函數
	}
}

//剛才直接打印的時候,是作為一個參數傳遞進去,運行到defer的時候是將這個i的值進行了一個拷貝,所以打印的是 2 1 0
//這種情況下i一直是一個地址的引用,i一直引用的是局部變量的i,在退出這個循環體的時候 i已經變成了3,在main函數return的時候,開始執行defer語句,defer語句的時候i已經變成了3

異常機制

package main

import (
	"fmt"
)

func main() {
	A()
	B()
	C()

}

func A() {
	fmt.Println("Func A")
}
func B() {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println("Recover in B")

		}
	}()
	panic("Panic in B")

}
func C() {
	fmt.Println("Func C")
}

package main

import (
	"fmt"
)

func main() {
	var fs = [4]func(){}
	for i := 0; i < 4; i++ {
		defer fmt.Println("defer i=", i) //這個i是傳遞進來的參數,所以是值得拷貝
		defer func() {
			fmt.Println("defer_closure i=", i) //這里的i是引用外部的i,所以循環結束后,i變成了4
		}()
		fs[i] = func() {
			fmt.Println("closure i = ", i) //這里也是引用外部的i,所以循環結束后i變成了4
		}
	}
	for _, f := range fs {
		f()
	}
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM