人永遠都無法知道自己該要什么葫辐,因為人只能活一次,既不能拿它跟前世相比汗贫,也不能在來生加以修正身坐。沒有任何方法可以檢驗?zāi)姆N抉擇是好的,因為不存在任何比較落包。一切都是馬上經(jīng)歷部蛇,僅此一次,不能準(zhǔn)備咐蝇。from 《不能承受的生命之輕》
引用于:http://wufazhuce.com/one/1380
函數(shù)在Go語言中應(yīng)該是最頻繁使用的類型了涯鲁,所以把Go語言定義為函數(shù)式編程語言,個人認(rèn)為也是一種正確的解釋。函數(shù)在Go中既有類似JS語言的用法抹腿,也有C岛请、JAVA這種語言的身影。
和JS語言對比警绩,函數(shù)有以下特性:
- 普通函數(shù):作為一個普通方法使用崇败。
- 變長參數(shù):函數(shù)可以接收可變數(shù)目的參數(shù),這在JAVA里面也有類似用法肩祥。
- 閉包函數(shù):JS中常用的一種模式僚匆,便于維護一些特殊作用域的變量等用法。
- 對象中的方法:JS中類面向?qū)ο蟮囊环N寫法搭幻。
和C語言對比,函數(shù)有以下特性:
- 多返回值:函數(shù)可以返回多個不同類型的結(jié)果逞盆。
Go自身定義的函數(shù)特性:
- 巧用return:合理的利用return中斷程序邏輯檀蹋,會使代碼顯得更簡潔易懂。
- 作為參數(shù)傳遞:作為回調(diào)函數(shù)來說云芦,更好的規(guī)范了回調(diào)函數(shù)引用的規(guī)范性俯逾。
- 值傳遞:變量作為參數(shù)傳遞給函數(shù)時,默認(rèn)是值傳遞舅逸,只是變量的一個拷貝桌肴,即副本。函數(shù)內(nèi)部只是對變量副本的操作琉历,不會影響到實際變量本身坠七。
普通函數(shù)、多返回值
// 普通函數(shù)旗笔,不帶參數(shù)
func generalFun() {
fmt.Println("普通函數(shù)")
}
// 普通函數(shù)彪置,帶參數(shù)
func generalFun1(i int, str string) {
fmt.Printf("i=%d str=%s\n", i, str)
}
// 普通函數(shù),帶參數(shù)和返回一個結(jié)果
func generalFun2(i int, str string) string {
tempStr := strconv.Itoa(i) + str
return tempStr
}
// 普通函數(shù)蝇恶,帶參數(shù)和返回多個結(jié)果拳魁,需要用括號包含所有返回類型
func generalFun3(i int, str string) (int, string) {
tempStr := strconv.Itoa(i) + str
tempI := i + i
return tempI, tempStr
}
上面示例中的普通函數(shù)用法,作為對象的方法也是同樣支持撮弧。
變長參數(shù)
格式:paramName ...type
:可理解為函數(shù)可以接收type
類型的可變參數(shù)潘懊,參數(shù)名稱為paramName
,在函數(shù)內(nèi)部可把paramName
理解為類似slice類型的參數(shù)值贿衍∈谥郏可用for _, val := range paramName
遍歷傳遞進來的多個參數(shù)。
1舌厨、第一種用法:知道可變參數(shù)的類型時:
func myfun(prefix string, who ...string) {
//獲取可變參數(shù)的長度
fmt.Println(len(who))
for _, val := range who {
fmt.Println(prefix, val)
}
}
func Test_fun_Sample01(t *testing.T) {
myfun("hello:", "Joe", "Anna", "Eileen")
}
Test_fun_Sample01 測試用例執(zhí)行結(jié)果:
hello: Joe
hello: Anna
hello: Eileen
2岂却、第二種用法:不知道可變參數(shù)的類型時:
如果一個變長參數(shù)的類型沒有被指定夭问,則可以使用默認(rèn)的空接口 interface{}
,這樣就可以接受任何類型的參數(shù)削饵。該方案不僅可以用于長度未知的參數(shù)劲件,還可以用于任何不確定類型的參數(shù)∩ǔ撸可使用for-range
循環(huán)以及switch
結(jié)構(gòu)對每個參數(shù)的類型進行判斷
type Person struct {
name string
}
func test8(values ...interface{}) {
for _, val := range values {
switch v := val.(type) {
case int:
fmt.Println("val type is int ", v)
case float64:
fmt.Println("val type is float ", v)
case string:
fmt.Println("val type is string ", v)
case bool:
fmt.Println("val type is bool ", v)
case Person:
fmt.Println("val type is Person ", v.name)
case *Person:
fmt.Println("val type is *Person ", v.name)
default:
fmt.Println("val type is unknow ", v)
}
}
}
func Test_test8(t *testing.T) {
temp_int := 1
temp_float := 5.6
temp_string := "hello"
temp_bool := true
temp_person1 := &Person{name: "jack"}
temp_person2 := Person{name: "rose"}
var temp_float32 float32 = 6.6
test8(temp_int, temp_float, temp_string, temp_bool, temp_person1, temp_person2, temp_float32)
}
運行結(jié)果:
val type is int 1
val type is float 5.6
val type is string hello
val type is bool true
val type is *Person jack
val type is Person rose
val type is unknow 6.6
示例中需要注意的兩個地方:
-
float
默認(rèn)定義的類型為float64
筋栋。 -
struct
要區(qū)分傳遞的參數(shù)是 Person 類型的結(jié)構(gòu)體變量,還是 Person 類型的指針變量正驻。
巧用return
1弊攘、第一種用法:無返回值函數(shù),在內(nèi)部任何程序執(zhí)行片段使用return
姑曙,直接會跳出該函數(shù)襟交,return
下面的程序片段都不會再執(zhí)行。
func fun_return() {
for i := 0; i < 10; i++ {
fmt.Printf("%v ", i)
if i == 3 {
return
}
}
} //0 1 2 3
2伤靠、第二種用法:有返回值函數(shù)捣域,在內(nèi)部任何程序執(zhí)行片段使用return
,它下面的程序片段都不會再執(zhí)行宴合,但是程序結(jié)尾必須以return
結(jié)束焕梅,否則程序報錯。
func fun_return() int {
for i := 0; i < 10; i++ {
fmt.Printf("%v ", i)
if i == 3 {
return
}
}
return i
} //0 1 2 3
上面兩個示例卦洽,執(zhí)行結(jié)果一樣贞言,但是唯一不同的就是有返回值函數(shù)程序最后必須有
return
。
值傳遞阀蒂、引用傳遞
任何時候该窗,如果要同步改變函數(shù)外聲明的變量的值時,都要優(yōu)先考慮用指針傳遞蚤霞,即占用內(nèi)存空間少挪捕,又執(zhí)行速度快。
基本類型的參數(shù)傳遞
func test3(str string) {
str = "go"
fmt.Printf("str=%s", str)
}
func test4(str *string) {
*str = "go"
fmt.Printf("str=%s", *str)
}
func Test_test3(t *testing.T) {
temp := "hello"
test3(temp)
fmt.Println(" temp=", temp) //str=go temp= hello
test4(&temp)
fmt.Println(" temp=", temp) //str=go temp= go
}
struct類型的參數(shù)傳遞争便,及對象傳遞
type Person struct {
name string
}
// 傳遞Person struct類型的變量
func test5(p Person) {
p.name = "go" // 此時的p為參數(shù)副本
fmt.Printf("Person.name = %s", p.name)
}
// 傳遞指向Person struct類型的的變量地址
func test6(p *Person) {
p.name = "go"
fmt.Printf("Person.name = %s", p.name)
}
func Test_test5(t *testing.T) {
p := Person{"hello"}
test5(p)
fmt.Println(" p.name=", p.name) //Person.name = go p.name= hello
test6(&p)
fmt.Println(" p.name=", p.name) //Person.name = go p.name= go
p1 := new(Person)
p1.name = "jack"
test6(p1)
fmt.Println(" p1.name=", p1.name) //Person.name = go p1.name= go
}