Golang——秒懂函数、参数、可变参数、匿名函数、回调函数、内置函数
函數(shù):
函數(shù)是結(jié)構(gòu)化編程的最小模塊單元。它將復(fù)雜的算法過程分解為若干較小任務(wù),隱藏相關(guān) 細節(jié),使得程序結(jié)構(gòu)更加清晰,易于維護。函數(shù)被設(shè)計成相對獨立,通過接收輸入?yún)?shù)完成一段算法指令,輸出或存儲相關(guān)結(jié)果。因此,函數(shù)還是代碼復(fù)用和測試的基本單元。
關(guān)鍵字func用于定義函數(shù)
- 函數(shù)必須先定義,后調(diào)用,定義的過程成為函數(shù)定義,
- 函數(shù)創(chuàng)建后并不是直接可以運行的,需要手動使用后,才執(zhí)行,該過程成為函數(shù)調(diào)用
Go函數(shù)特點:
- 無需前置聲明
- 不支持命名嵌套定義(nested)
- 不支持同名函數(shù)重載(overload)
- 不支持默認參數(shù)
- 支持可變參數(shù)
- 支持多返回值
- 支持命名返回值
- 支持匿名函數(shù)和閉包
函數(shù)只能判斷是否為nil,不支持其他比較操作
func a() {} func b() {} func main() {println(a == nil)println(a == b) // 無效操作:a == b (func can only be compared to nil }參數(shù):
函數(shù)傳參都是值傳遞,實參將自己的地址值拷貝一份給形參。如果參數(shù)是引用類型,那么就會改變實參的值(A棧幀修改B棧幀的值)
定義格式:
參數(shù):由數(shù)據(jù)類型和變量名組成 數(shù)據(jù)類型 變量名
例: int a
普通格式調(diào)用:
函數(shù)名(參數(shù)1,參數(shù)2)函數(shù)值格式調(diào)用:
變量 := 對象.函數(shù)名函數(shù)表達式格式調(diào)用:
變量 := 類名.函數(shù)名函數(shù)調(diào)用時,參數(shù)的數(shù)量與類型必須與函數(shù)定義中的設(shè)置相匹配,否則程序?qū)箦e。函數(shù)的返回值通常會使用變量接收,否則該返回值將無意義
演示:
func main() {person := Person{24}// 普通格式person.show()// 函數(shù)值格式p1 := person.showp1()// 函數(shù)表達式格式p2 := (*Person).showp2(&person) }type Person struct {age int }不支持有默認值的可選參數(shù),不支持命名實參。調(diào)用時必須按簽名順序傳遞指定類型和數(shù)量的實參,就算以“_”命名也不能忽略。參數(shù)列表中,相鄰的同類型參數(shù)可合并
func main() {test(1, 2, "abc") // 報錯: not enough arguments in call to test 給bool變量賦值就可以了 }func test(x, y int, s string, _ bool) *int {return nil }參數(shù)可視作函數(shù)的局部變量,因此不能在相同層次定義同名變量
func add(x, y int) int {x := 100 // 錯誤:':=' 的左側(cè)沒有新變量var y int // 錯誤:未使用的變量 'y'return x + y }不管是指針、引用類型,還是其他類型參數(shù),都是值拷貝傳遞,無非是拷貝目標對象,還是拷貝指針自身而已。在函數(shù)調(diào)用時,會為形參和返回值分配內(nèi)存空間, 并將實參數(shù)據(jù)拷貝到形參內(nèi)存。
func main() {a := 0x100p := &aprintln(&p, p) //輸出實參p的地址。test(p) }func test(x *int) {println(&x, x) //輸出形參x的地址。 }輸出:
從輸出結(jié)果可以看出,盡管實參和形參都指向同一目標,但指針自身依然被復(fù)制。
要實現(xiàn)傳出參數(shù),通常建議使用返回值。當然,二級指針也是常用的手法
func main() {var p *inttest(&p)println(*p) }func test(p **int) {x := 100 * p = &x }如果函數(shù)參數(shù)過多,建議將其重構(gòu)為一個復(fù)合類型參數(shù),也算是變向?qū)崿F(xiàn)可選參數(shù)和命名實參功能
func main() {opt := newOption()opt.port = 8085server(opt) }type serverOption struct {address stringport intpath stringtimeout time.Durationlog *log.Logger }func newOption() *serverOption {return &serverOption{address: "0.0.0.0",port: 8080,path: "/var/test",timeout: time.Second * 5,log: nil,} }func server(option *serverOption) {}形參和實參:
形參是指函數(shù)定義中的參數(shù),實參則是函數(shù)調(diào)用時所傳遞的參數(shù)。形參類同函數(shù)局部變量,而實參則函數(shù)外部對象,可以是常量、變量、表達式或函數(shù)等。
形參:
函數(shù)定義中的參數(shù),顧名思義就是只有一個形式,沒有具體的數(shù)值
等同于變量定義格式,例如:var num int
實參:
函數(shù)調(diào)用中的參數(shù),顧名思義就是有實際的參數(shù)數(shù)值
等同于使用變量或常量,例如:var num int =10
傳遞的形參即使在函數(shù)中操作,也不會影響原來的數(shù)據(jù)值
func main() {a, b := 1, 2Demo12(a, b)fmt.Println("main:", a, b) //main: 1 2 } func Demo12(a, b int) {a, b = b, afmt.Println("Demo12:", a, b) //Demo12: 2 1可變參數(shù)(不定參數(shù)):
寫函數(shù)的時候如果不知道形參長度,可以使用可變參數(shù)傳遞
變參本質(zhì)上就是一個slice,它只能接收相同類型的參數(shù)值,且必須放在參數(shù)列表的最后
圖解:
演示:
變參是slice,那么參數(shù)復(fù)制的僅是slice struct自身,不包括底層數(shù)組,因此我們可以修改原數(shù)據(jù)。如果需要,可使用內(nèi)置函數(shù)copy復(fù)制slice數(shù)據(jù)
func test(a ...int) {for i := range a {a[i] += 100} } func main() {a := []int{10, 20, 30}test(a...)fmt.Println(a) }返回值:
函數(shù)可以有返回值,也可以有返回值,也可以定義多個返回值。使用return關(guān)鍵字定義
如果沒有定義返回值,但是寫了return,那么就相當于終止了函數(shù),跳出循環(huán)的意思
方式一:
func main() {// 定義變量接收返回值并執(zhí)行打印r := Demo06(1, 2)println(r) }// 要在參數(shù)后面加上返回值的數(shù)據(jù)類型 func Demo06(a int, b int) int {// 將a+b的結(jié)果賦值給sum并返回,需要在函數(shù)里再定義返回值sum := a + breturn sum }方式二:
func main() {// 定義變量接收返回值并執(zhí)行打印r := Demo07(1, 2)println(r) }// 要在參數(shù)后面加上返回值和返回值的數(shù)據(jù)類型,不需要在函數(shù)里再定義返回值 func Demo07(a int, b int) (sum int) {sum = a + breturn sum }方式三:
func main() {// 定義變量接收返回值并執(zhí)行打印r := Demo08(1, 2)println(r) }// 要在參數(shù)后面加上返回值和返回值的數(shù)據(jù)類型,不需要在函數(shù)里再定義返回值 func Demo08(a int, b int) (sum int) {sum = a + b// 如果在函數(shù)上已經(jīng)指定了返回值,在函數(shù)中可以省略返回值,直接returnreturn }函數(shù)返回多個返回值:
func main() {// 定義變量接收返回值a, b := Demo09()fmt.Println("a = ", a, "\n b = ", b) }// 在函數(shù)上只寫返回值類型,在函數(shù)中寫返回值 func Demo09() (int, int) {a := 1b := 2// 返回兩個變量的值return a, b }有返回值的函數(shù),必須有明確的return終止語句
func test(x int) int {if x > 0 {return -1} else if x < 0 {return -1} } // 錯誤:missing return at end of function特例情況是panic,或者無break的死循環(huán)
func test(x int) int {for {break} }借鑒自動態(tài)語言的多返回值模式,讓函數(shù)得以返回更多狀態(tài),尤其是error模式。
import "errors" func div(x, y int) (int, error) { //多返回值列表必須使用括號。if y == 0 {return 0, errors.New("division by zero")}return x / y, nil }相同類型的多返回值可用作調(diào)用實參,或直接返回
func main() {log(test()) //多返回值直接用作實參。 } func test() (int, error) {return div(5, 0) //多返回值直接用作return結(jié)果。 }func div(x, y int) (int, error) {if y == 0 {return 0, errors.New("division by zero")}return x / y, nil }func log(x int, err error) {fmt.Println(x, err) }返回值命名:
從這個簡單的示例就可看出,命名返回值讓函數(shù)聲明更加清晰,同時也會改善幫助文檔和 代碼編輯器提示。
func paging(sql string, index int) (count int, pages int, err error) {}命名返回值和參數(shù)一樣,可當做函數(shù)局部變量使用,最后由return隱式返回
func div(x, y int) (z int, err error) {if y == 0 {err = errors.New("division by zero")return}z = x / yreturn // 相當于 “return z, err”。 }只是這些特殊的局部變量會被不同層級的同名變量遮蔽,造成潛在錯誤。好在編譯器會自動檢查此類狀況,只需改為顯式return返回即可
func add(x, y int) (z int) {{z := x + y //新定義的同名局部變量,同名遮蔽。return // 錯誤:z is shadowed during return (改成 “return z” 即可)}return }除遮蔽外,我們還必須對全部返回值命名,否則編譯器會稀里糊涂
func test() (int, s string, e error) {return 0, "", nil // 錯誤:cannot use 0 (type int) as type string in return argument }函數(shù)重寫:
子類中出現(xiàn)與父類一模一樣的函數(shù)時,會使用就近原則,子類優(yōu)先使用子類的函數(shù),要想通過子類的對象調(diào)用父類的函數(shù),需要使用子類對象.父類.函數(shù)名
函數(shù)重寫的應(yīng)用場景:
當子類需要父類的功能,而功能主體子類有自己特有內(nèi)容時,可以重寫父類中的函數(shù),這樣,即沿襲了父類的功能,又定義了子類特有的內(nèi)容
演示:
func main() {stu := Student{}stu.show() // 子類的Show函數(shù)stu.Person.show() // 父類的Show函數(shù) }type Person struct {age int }type Student struct {Personname stringage intscore float64 }func (student Student) show() {fmt.Println("子類的Show函數(shù)") }func (person *Person) show() {fmt.Println("父類的Show函數(shù)") }匿名函數(shù):
匿名函數(shù)是指沒有定義名字符號的函數(shù)
匿名函數(shù)除沒有名字外,其他和普通函數(shù)完全相同。最大的區(qū)別是,我們可在函數(shù)內(nèi)部定義匿名函數(shù),形成類似嵌套函數(shù)的效果。匿名函數(shù)可直接調(diào)用,保存到變量,作為參數(shù)或返回值
格式:
func(形參) {函數(shù)體}(實參)直接執(zhí)行:
func main() {func(s string) {fmt.Println(s)}("我是實參,上面的s,是形參,我會被打印不") }有返回值匿名函數(shù):
func main() {add := test()println(add(1, 2)) }func test() func(int, int) int {return func(x, y int) int {return x + y} }作為第一類對象,不管是普通函數(shù),還是匿名函數(shù)都可作為struct字段,或通過channel傳遞
func testStruct() {type calc struct {mul func(x, y int) int}x := calc{mul: func(x, y int) int {return x * y},}println(x.mul(2, 3)) } func testChannel() {c := make(chan func(int, int) int, 2)c <- func(x, y int) int {return x + y}println((<-c)(1, 2)) }回調(diào)函數(shù):
本質(zhì)就是函數(shù)類型的指針,通過指針,在特定位置,滿足條件后,系統(tǒng)會自動調(diào)用函數(shù)。
普通函數(shù)調(diào)用格式:
func aaa(){}func main(){aaa()// 普通函數(shù)是通過函數(shù)名+括號調(diào)用的 }回調(diào)函數(shù)調(diào)用格式:
func handler(w http.ResponseWriter, r *http.Request) {}func main(){http.HandleFunc("/itzhuzhu", handler)// 只需要將函數(shù)名傳入即可 }內(nèi)置函數(shù):
len:用來計算長度的,string、arr、slice、map、channel都可以
new:用來分配值內(nèi)存的,int、float32、struct返回值是指針
make:用來分配引用類型內(nèi)存的,slice、map、channel
直接定義變量的流程是:開辟內(nèi)存空間->將數(shù)據(jù)存儲到內(nèi)存空間
適用new定義變量的流程是:開啟指針內(nèi)存空間->指向數(shù)據(jù)的內(nèi)存地址
總結(jié)
以上是生活随笔為你收集整理的Golang——秒懂函数、参数、可变参数、匿名函数、回调函数、内置函数的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 三维数组设置索引_python3三维数据
- 下一篇: Golang——枚举(iota)的使用