10-函式-1-函式簡介

行走的皮卡丘發表於2020-11-18

一 函式

1.1 函式宣告

函式宣告格式:

func 函式名字 (引數列表) (返回值列表){
	// 函式體
	return 返回值列表
}

注意:

  • 函式名首字母小寫為私有,大寫為公有;
  • 引數列表可以有0-多個,多引數使用逗號分隔,不支援預設引數;
  • 返回值列表返回值型別可以不用寫變數名
  • 如果只有一個返回值且不宣告型別,可以省略返回值列表與括號
  • 如果有返回值,函式內必須有return

Go中函式常見寫法:

//無返回值,預設返回0,所以也可以寫為 func fn() int {}
func fn(){}  

//Go推薦給函式返回值起一個變數名
func fn1() (result int) {
	return 1
}

//第二種返回值寫法
func fn2() (result int) {
	result = 1
	return 
}

//多返回值情
func fn3() (int, int, int) {
   return 1,2,3
}

//Go返回值推薦多返回值寫法:
func fn4() (a int, b int, c int) {		多個引數型別如果相同,可以簡寫為: a,b int
   a , b, c = 1, 2, 3
   return 
}

1.2 值傳遞和引用傳遞

不管是值傳遞還是引用傳遞,傳遞給函式的都是變數的副本,不同的是,值傳遞的是值的拷貝,引用傳遞的是地址的拷貝,一般來說,地址拷貝效率高,因為資料量小,而值拷貝決定拷貝的 資料大小,資料越大,效率越低。

如果希望函式內的變數能修改函式外的變數,可以傳入變數的地址&,函式內以指標的方式操作變數。

1.3 可變引數

可變引數變數是一個包含所有引數的切片。如果要在多個可變引數中傳遞引數 ,可以在傳遞時在可變引數變數中預設添 加“ …”,將切片中的元素進行傳遞,而不是傳遞可變引數變數本身。

示例:對可變引數列表進行遍歷

func joinStrings(slist ...string) string {
	var buf bytes.Buffer
	for _, s := range slist {
		buf.WriteString(s)
	}
	return buf.String()
}

func main() {
	fmt.Println(joinStrings("pig", " and", " bird"))
}

示例:引數傳遞

// 實際列印函式
func rawPrint(rawList ...interface{}) {
	for _, a := range rawList {
		fmt.Println(a)
	}
}

// 封裝列印函式
func print(slist ...interface{})  {
	// 將slist可變引數切片完整傳遞給下一個函式
	rawPrint(slist...)
}

func main() {
	print(1,2,3)
}

1.4 匿名函式

匿名函式可以看做函式字面量,所有直接使用函式型別變數的地方都可以由匿名函式代替。匿名函式可以直接賦值給函式變數,可以當做實參,也可以作為返回值使用,還可以直接被呼叫。

func main()  {

   a := 3
   f1 := func(num int) {    		// f1 即為匿名函式
      fmt.Println(num) 			// 匿名函式訪問外部變數
   }
   f1(a)

   func() {         			// 匿名函式自調
      fmt.Println(a)
   }()

}

//匿名函式實戰:取最大值,最小值
x, y := func(i,j int) (max,min int) {
   if i > j {
      max = i
      min = j
   } else {
      max = j
      min = i
   }
   return
}(10,20)
fmt.Println(x + ' ' + y)

1.5 函式型別

函式去掉函式名、引數名和{}後的結果即是函式型別,可以使用%T列印該結果。

兩個函式型別相同的前提是:擁有相同的形參列表和返回值列表,且列表元素的次序、型別都相同,形參名可以不同。

示例:

func mathSum(a, b int) int {
	return a + b
}

func mathSub(a, b int) int {
	return a - b
}

//定義一個函式型別
type MyMath func(int, int) int

//定義的函式型別作為引數使用
func Test(f MyMath, a , b int) int{
	return f(a,b)
}

通常可以把函式型別當做一種引用型別,實際函式型別變數和函式名都可以當做指標變數,只想函式程式碼開始的位置,沒有初始化的函式預設值是nil。

二 Go函式特性總結

  • 支援有名稱的返回值;
  • 不支援預設值引數;
  • 不支援過載;
  • 不支援命名函式巢狀,匿名函式可以巢狀;
  • Go函式從實參到形參的傳遞永遠是值拷貝,有時函式呼叫後實參指向的值發生了變化,是因為引數傳遞的是指標的拷貝,實參是一個指標變數,傳遞給形參的是這個指標變數的副本,實質上仍然是值拷貝;
  • Go函式支援不定引數;

三 兩個特殊函式

3.1 init函式

Go語言中,除了可以在全域性宣告中初始化實體,也可以在init函式中初始化。init函式是一個特殊的函式,它會在包完成初始化後自動執行,執行優先順序高於main函式,並且不能手動呼叫init函式,每一個檔案有且僅有一個init函式,初始化過程會根據包的以來關係順序單執行緒執行。

package main
import (
	"fmt"
)
func init() {
	//在這裡可以書寫一些初始化操作
	fmt.Println("init...")
}
func main() {
	fmt.Println("main...")
}

3.2 new函式

new函式可以用來建立變數。表示式new(T)將建立一個T型別的匿名變數,初始化為T型別的零值,然後返回變數地址,返回的指標型別為*T

	p := new(int)		// p 為 *int型別,只想匿名的int變數
	fmt.Println(*p)		// "0"
	*p = 2				// 設定 int匿名變數值為2
	fmt.Println(*p)

new函式還可以用來為結構體建立例項:

type file struct {

}
f := new(file)

貼士:new函式其實是語法糖,不是新概念,如下所示的兩個函式其實擁有相同的行為。

func newInt1() *int {
	return new(int)
}

func newInt2() *int {
	var dummy int
	return &dummy
}

注意:new只是一個預定義函式,並不是一個關鍵字,所以new也有可能會被專案定義為別的型別。

3.3 make函式

make函式經常用來建立切片、Map、管道:

	m1 := map[string]int{}
	m2 := make(map[string]int, 10)

上面展示了兩種map的建立方式,其不同點是第一種建立方式無法預估長度,當長度超過了當前長度時,會引起記憶體的拷貝!!第二種建立方式直接限定了長度,這樣能有效提升效能!

相關文章