Go語言中的函數(shù)詳解
1.函數(shù)的聲明定義
//func關(guān)鍵字 //getStudent函數(shù)名 //(id int, classId int) 參數(shù)列表 //(name string,age int) 返回值列表 func getStudent(id int, classId int)(name string,age int) { //函數(shù)體 if id==1&&classId==1{ name = "BigOrange" age = 26 } //返回值 return name, age // 支持多重返回值 }
有意思的是Go語言的返回值可以有多個(gè),并且放在了參數(shù)列表后面,而C#等都是在函數(shù)名之前,也沒有關(guān)鍵字。
2.函數(shù)的調(diào)用
import "fmt" //調(diào)用fmt包中的Println方法。 fmt.Println("Name:", std.Name, "Age:",std.Age)
3.函數(shù)編寫的原則
很好奇為什么沒有public private等關(guān)鍵字,那函數(shù)怎么才能定義為公用和私有呢?
Go語言有這樣的規(guī)則:小寫字母開頭的函數(shù)只在本包內(nèi)可見,大寫字母開頭的函數(shù)才
能被其他包使用。這個(gè)規(guī)則也適用于類型和變量的可見性。
4.不定參數(shù)問題
不定參數(shù)是指函數(shù)傳入的參數(shù)個(gè)數(shù)為不定數(shù)量。
func myfunc(args ...int) { for _, arg := range args { fmt.Println(arg) } }
函數(shù)myfunc()接受不定數(shù)量的參數(shù),這些參數(shù)的類型全部是int
※形如...type格式的類型只能作為函數(shù)的參數(shù)類型存在,并且必須是最后一個(gè)參數(shù)。
它是一個(gè)語法糖(syntactic sugar),即這種語法對(duì)語言的功能并沒有影響,但是更方便程序員使用。
從內(nèi)部實(shí)現(xiàn)機(jī)理上來說,類型...type本質(zhì)上是一個(gè)數(shù)組切片,也就是[]type,這也是為
什么上面的參數(shù)args可以用for循環(huán)來獲得每個(gè)傳入的參數(shù)。
不定參數(shù)的傳遞
myfunc3(args ...int)
對(duì)應(yīng)上面的這個(gè)函數(shù),傳遞參數(shù)可以為下面兩種
// 按原樣傳遞 myfunc3(args...) // 傳遞片段,實(shí)際上任意的int slice都可以傳進(jìn)去 myfunc3(args[1:]...)
任意類型的不定參數(shù)
可以看到 fmt.Println()方法接受了不定參數(shù),但它是 ...interface{}
用interface{}傳遞任意類型數(shù)據(jù)是Go語言的慣例用法。
5.多返回值
Go語言函數(shù)可以返回多個(gè)返回值
如果調(diào)用方調(diào)用了一個(gè)具有多返回值的方法,但是卻不想關(guān)心其中的某個(gè)返回值,可以簡(jiǎn)單
地用一個(gè)下劃線“_”來跳過這個(gè)返回值
6.匿名函數(shù)
Go語言支持隨時(shí)在代碼里定義匿名函數(shù)。
匿名函數(shù)由一個(gè)不帶函數(shù)名的函數(shù)聲明和函數(shù)體組成
func(a, b int, z float64) bool { return a*b <int(z) }
匿名函數(shù)可以直接賦值給一個(gè)變量或者直接執(zhí)行:(有點(diǎn)像js哈)
f := func(x, y int) int { return x + y }
func(ch chan int) { ch <- ACK } (reply_chan) // 花括號(hào)后直接跟參數(shù)列表表示函數(shù)調(diào)用
7.閉包
package main import ( "fmt" ) func main() { 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 a() }
結(jié)果:
i, j: 10, 5
i, j: 10, 10
分析:
1---"...func()(func()) {....."
表明此匿名函數(shù)返回值的類型是func(), 即此匿名函數(shù)返回一個(gè)函數(shù)指針(此處引用一下c 的概念);
2---"...returnfunc() {
fmt.Printf("i, j: %d, %d\n", i, j)
}..."
表明返回的函數(shù)指針指向一個(gè)打印i, j: %d, %d\n的函數(shù);
3---"...a := func()(func()) {
...
}()..."
末尾的括號(hào)表明匿名函數(shù)被調(diào)用,并將返回的函數(shù)指針賦給變量a ;
綜合來看:
"...a := func()(func()) { var i int = 10 return func() { fmt.Printf("i, j: %d, %d\n", i, j) } }()..."
此代碼片段的意思"等價(jià)于"
a := func() { fmt.Printf("i, j: %d, %d\n", i, j) }
至于為何要用匿名函數(shù)如此的轉(zhuǎn)一圈,是因?yàn)橐瞄]包的概念,此概念省略不表,多寫點(diǎn)代碼試試就能體會(huì)了。
補(bǔ)充:傳值和傳引用
1.type 定義一個(gè)類型,有兩種方式
- ①配合struct,創(chuàng)建一個(gè)新的結(jié)構(gòu),類似C#里面的Class
- ②配合既存的類型(int64...),創(chuàng)建一個(gè)新的類型,類似C++里面的typedef
2.Struct的如果不進(jìn)行賦值,會(huì)使用0值進(jìn)行初始化。
3.type使用既存類型定義新結(jié)構(gòu),和既存類型不是同一個(gè)類型,不能進(jìn)行轉(zhuǎn)換,例如
package main type Integer int64 func main() { var srcInt Integer srcInt = int64(1000) }
結(jié)果:
4.方法和函數(shù)的區(qū)別
方法能給用戶定義的類型添加新的行為。方法實(shí)際上也是函數(shù),只是在聲明時(shí),在關(guān)鍵字func 和方法名之間增加了一個(gè)參數(shù)
例如:
這是函數(shù),它的調(diào)用就是直接在使用的時(shí)候 傳入?yún)?shù)獲取返回值就行
func getStudentName(student Student)(name string) { //返回值 return student.Name }
這是方法
package main import ( "fmt" ) type Student struct { Name string Age int } //Student類的方法 使用值接收者實(shí)現(xiàn)了一個(gè)方法 func (student Student) getStudentName()(string){ return student.Name } //Student類的方法 使用指針接收者實(shí)現(xiàn)了一個(gè)方法 func (student *Student) changeStudentName(name string){ student.Name = name fmt.Println("方法執(zhí)行之后name",student.Name) } //Student類的方法 使用指針接收者實(shí)現(xiàn)了一個(gè)方法 func (student Student) changeStudentNameByValue(name string){ student.Name = name fmt.Println("方法執(zhí)行之后name",student.Name) } func main() { bigOrange:=Student{ Name:"BigOrange",Age:18, } bigApple:=Student{ Name:"BigApple",Age:20, } //使用函數(shù)獲取學(xué)生名稱 name1 := getStudentName(bigOrange) name2 := getStudentName(bigApple) fmt.Println("========通過傳地址ChangeName之后==========") fmt.Println("方法執(zhí)行之前name",name1) bigOrange.changeStudentName("BigBanana") name1 = bigOrange.getStudentName() fmt.Println("方法返回之后Name",name1) fmt.Println("========通過傳值ChangeName之后===========") fmt.Println("方法執(zhí)行之前name",name2) bigApple.changeStudentNameByValue("BigPear") name2 = bigApple.getStudentName() fmt.Println("方法返回之后Name",name2) }
結(jié)果:
========通過傳地址ChangeName之后==========
方法執(zhí)行之前name BigOrange
方法執(zhí)行之后name BigBanana
方法返回之后Name BigBanana
========通過傳值ChangeName之后===========
方法執(zhí)行之前name BigApple
方法執(zhí)行之后name BigPear
方法返回之后Name BigApple
上面的例子中
分別使用了函數(shù)和方法進(jìn)行獲取學(xué)生姓名
分別使用了傳值和傳地址的方式對(duì)學(xué)生名稱進(jìn)行修正
綜上
- ① Go語言中的方法,相比于函數(shù),多了一個(gè)接收者參數(shù)
- ② Go 語言里有兩種類型的接收者:值接收者和指針接收者
方法如果使用 值接收者,那么調(diào)用者可以是值接收者類型、也可以是它的指針類型,請(qǐng)看下面的例子(補(bǔ)充上例):
fmt.Println("========使用指針來調(diào)用值類型聲明的接收者方法===========") bigGrape := &Student{ Name:"bigGrape",Age:22} //取地址!?。?! name3 := bigGrape.getStudentName(); fmt.Println("========通過傳值ChangeName之后===========") fmt.Println("方法執(zhí)行之前name",name3) bigGrape.changeStudentNameByValue("BigXXXX") name3 = bigGrape.getStudentName() fmt.Println("方法返回之后Name",name3)
結(jié)果:
========使用指針來調(diào)用值類型聲明的接收者方法===========
name bigGrape
========通過傳值ChangeName之后===========
方法執(zhí)行之前name bigGrape
方法執(zhí)行之后name BigXXXX
方法返回之后Name bigGrape
如上代碼 使用了&獲取地址,所以bigGrape是一個(gè)student類型的指針。下面的代碼和上例一樣,直接使用了【變量.方法】的方式調(diào)用方法,結(jié)果也和值類型調(diào)用的一樣。
為什么?
【Go 在代碼背后的執(zhí)行動(dòng)作】
name4:=(*bigGrape).getStudentName()
Go 編譯器為了支持這種方法調(diào)用背后做的事情?!局羔槺唤庖脼橹怠浚@樣就符合了值接收者的要求。再?gòu)?qiáng)調(diào)一次,getStudentName 操作的是一個(gè)副本,只不過這次操作的是從bigGrape指針指向的值的副本。
同理還記得上面的,這句代碼嗎?對(duì)bigOrange這個(gè)變量修改了名稱
bigOrange.changeStudentName("BigBanana")
bigOrange和bigApple明顯是值類型,但是 changeStudentName 接收者是一個(gè)指針類型,為什么能調(diào)用,也是基于Go代碼背后的執(zhí)行動(dòng)作,將值類型取了地址。
(&bigOrange).changeStudentName("BigOrange")
所以對(duì)于Go語言調(diào)用方法的類型,使用值或者指針都是可以的,不用拘泥于類型。
到此這篇關(guān)于Go語言函數(shù)的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持本站。
版權(quán)聲明:本站文章來源標(biāo)注為YINGSOO的內(nèi)容版權(quán)均為本站所有,歡迎引用、轉(zhuǎn)載,請(qǐng)保持原文完整并注明來源及原文鏈接。禁止復(fù)制或仿造本網(wǎng)站,禁止在非www.sddonglingsh.com所屬的服務(wù)器上建立鏡像,否則將依法追究法律責(zé)任。本站部分內(nèi)容來源于網(wǎng)友推薦、互聯(lián)網(wǎng)收集整理而來,僅供學(xué)習(xí)參考,不代表本站立場(chǎng),如有內(nèi)容涉嫌侵權(quán),請(qǐng)聯(lián)系alex-e#qq.com處理。