函數(shù)
在GO語言中吠式,函數(shù)的基本組成為func關(guān)鍵字陡厘,函數(shù)名,參數(shù)列表特占,返回值糙置,函數(shù)體和返回語句。
1. 函數(shù)的定義
首先是目,我們通過一個最簡單的加法函數(shù)來進行說明
package mymath
import "errors"
func Add(a int, b int) (ret int, err error) {
if a < 0 || b < 0 { //假定函數(shù)只支持兩個非負(fù)數(shù)相加
err = errors.New("Should be non-negative numbers")
return
}
return a + b, nil //支持多返回值
}
上面的函數(shù)中谤饭,(ret int, err error)
表示多返回值,error是Go中特有的錯誤處理包懊纳。
后面我們會單獨介紹這個包揉抵。
2. 函數(shù)調(diào)用
Go語言的函數(shù)調(diào)用非常簡單,只要事先導(dǎo)入了該包嗤疯。我們就可以按照如下方式調(diào)用冤今。
import "mymath" //第1節(jié)中,我們定義了mymath包茂缚,這里我們導(dǎo)入這個包
c := mymath.Add(1, 2)
注意在Go語言中戏罢,函數(shù)名以大寫字母開頭,才會被包導(dǎo)出脚囊,如果你不小心將上面的函數(shù)寫為
func add(a int, b int)
帖汞,這樣在mymath.add(1, 2)
使用時,編譯器會告訴你無法
找到add函數(shù)凑术。也就是說翩蘸,小寫字母開頭的函數(shù),在包外部是不可引用的淮逊。
3. 不定參數(shù)
在C語言時代催首,大家都用過printf()函數(shù)扶踊,不定參數(shù)帶給我們的方便想必大家已經(jīng)領(lǐng)略過了。下面
我們看看Go語言是如何支持不定參數(shù)的郎任。
3.1 不定參數(shù)類型
我們首先把函數(shù)定義為接收不定參數(shù)類型:
func myfunc(args ...int) {
for _, arg := range args {
fmt.Println(arg)
}
}
這段代碼的意思是myfunc函數(shù)接收不定數(shù)量的參數(shù)秧耗,這些參數(shù)全是int,所以要按照如下方式
調(diào)用:
myfunc(1, 2, 3, 4)
myfunc(2, 3, 5)
在上面的不定參數(shù)定義中舶治,形入...type格式的類型只能作為函數(shù)的參數(shù)類型存在分井。并且必須是
最后一個參數(shù)。\
語法糖(syntactic sugar) ...type這樣的語法對語言的功能并沒有影響霉猛,但是更方便程序員
的調(diào)用尺锚。通常來說,使用語法糖能夠增加程序的可讀性惜浅,從而減少程序出錯的機會瘫辩。
從內(nèi)部實現(xiàn)上來說,類型...type本質(zhì)上是一個數(shù)組切片坛悉,也就是[]type伐厌,這也是上面為什么能
用range遍歷的原因。
如果沒有...type語法糖裸影,則開發(fā)者就必須這樣寫:
func myfunc(args []int) {
for _, arg := range args {
fmt.Println(arg)
}
}
從函數(shù)的角度看挣轨,沒有語法糖并沒有什么影響,只是寫法不同而已轩猩。
但是卷扮,從調(diào)用的角度來看,情況就完全不同界轩。
myfunc([]int{1, 2, 3,4})
對比使用...type語法糖實現(xiàn)來看,調(diào)用時的寫法會打不相同衔瓮,語法糖使我們使用不定參數(shù)
更加便捷浊猾。
3.2 不定參數(shù)的傳遞
假設(shè)有另一個不定函數(shù)叫做myfunc2(arg ...int).
func myfunc(args ...int) {
myfunc2(args...) //按原樣傳遞
myfunc2(args[1:]...) //slice的特性在這里也可以使用
}
3.3 任意類型的不定參數(shù)
上面的例子中,我們將不定參數(shù)類型定為int型热鞍。下面我們來看看傳遞任意類型的實現(xiàn)葫慎。
我們不定參數(shù)類型知道為interface{},下面是fmt.Printf()函數(shù)原型
func Printf(format string, args ...interface{})
下面代碼展示了如何分離傳入的不同類型數(shù)據(jù)
package main
import "fmt"
func MyPrintf(args ...interface{}) {
for _, arg := range args {
switch arg.(type) {
case int:
fmt.Println(arg, "is an int value.")
case string:
fmt.Println(arg, "is an string value.")
case int64:
fmt.Println(arg, "is an int64 value.")
case default:
fmt.Println(arg, "is an unknown value.")
}
}
}
4. 多返回值
不同于C語言薇宠,Go語言一個函數(shù)可以有多個返回值偷办,這樣能讓我們的代碼更加簡潔。
比如File.Read()函數(shù)澄港,可以同時返回讀取的字節(jié)數(shù)和錯誤信息椒涯,如果讀取成功,則返回
值中的n為讀取的字節(jié)數(shù)回梧,err為nil废岂。否則祖搓,err為錯誤信息。
Func (file *File) Read(b []byte) (n int, err error) {}
Go語言不需要強制命名返回值湖苞,但是命名返回值可以讓代碼更清晰拯欧,也可用于文檔。
如果我們不想使用某個返回值财骨,可以用 "_"忽略镐作,例如:
n, _ := f.Read(buf) //忽略返回的錯誤信息
5. 匿名函數(shù)與閉包
匿名函數(shù)是指不需要定義函數(shù)名的一種函數(shù)實現(xiàn)方式。
5.1 匿名函數(shù)
匿名函數(shù)定義如下
func(a, b int, z float64) bool {
return a*b < int(z)
}
匿名函數(shù)可以直接賦值給一個變量
f := func(x, y int) int {
return x + y
}
func (ch chan int) {
ch <- ACK
} (reply_chan) //花括號后面跟參數(shù)列表隆箩,表示函數(shù)調(diào)用
5.2 閉包
匿名函數(shù)是一個閉包该贾。下面我們來了解一下閉包的概念,價值和應(yīng)用場景摘仅。
基本概念
閉包是可以包含自由(未綁定到特定對象)變量的代碼塊靶庙。這些變量不在這個代碼塊內(nèi)或者
任何全局上下文,而是在定義代碼塊的環(huán)境中定義娃属。要執(zhí)行的代碼塊為自由變量提供綁定的計算
環(huán)境(作用域)
閉包的價值
閉包的價值在于可以作為函數(shù)對象或者匿名函數(shù)六荒。對象類型系統(tǒng)而言,這意味著不僅要表示數(shù)據(jù)矾端,
還要表示代碼掏击。這些函數(shù)可以存儲到變量中作為參數(shù)傳遞給其他函數(shù),最重要的是能夠被函數(shù)
動態(tài)創(chuàng)建和返回秩铆。
Go語言中的閉包
Go語言中的閉包同樣也會引用到函數(shù)外的變量砚亭。閉包的實現(xiàn)確保只要閉包還被使用,那么殴玛,被閉包
引用的變量會一直存在捅膘。
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()
}
上述代碼執(zhí)行的結(jié)果為:
i, j: 10, 5
i, j: 10, 7
上面的例子中,變量a指向的閉包函數(shù)引用了局部變量i和j滚粟,i的值被隔離寻仗,在閉包之外不可被
修改。只要匿名函數(shù)內(nèi)部才能修改i的值凡壤,保證了i的安全性署尤。