溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

golang學(xué)習(xí)筆記-func函數(shù)

發(fā)布時(shí)間:2020-08-03 20:34:09 來(lái)源:網(wǎng)絡(luò) 閱讀:10891 作者:beyondlee2011 欄目:開(kāi)發(fā)技術(shù)

函數(shù)function

    - Go函數(shù)不支持 嵌套、重載和默認(rèn)參數(shù)

    - 但支持以下特性:

        無(wú)需聲明原形、不定長(zhǎng)變參、多返回值、命令返回值參數(shù)、匿名函數(shù)、閉包

    - 定義函數(shù)使用關(guān)鍵字func,且大括號(hào)不能另起一行(所有有大括號(hào)的均遵循此原則)

    - 函數(shù)也可以作為一種類型的使用,直接賦值給變量(匿名函數(shù))

定義一個(gè)函數(shù)

    格式:func name( 傳入的變量1 類型,變量2 類型 ) [ 返回變量 類型,變量 類型 ]{ }

    - 傳入的變量可以沒(méi)有,也可以使多個(gè)

    - 當(dāng)傳入的變量類型相同時(shí),可以全部省略只留最后一個(gè)

        func a(a,b,c int) {}

    - 返回值可以有多個(gè),返回值類型相同,也可以只留最后一個(gè),其中返回變量名稱可以省略,省略的話,就需要每返回一個(gè)寫(xiě)一個(gè)變量的類型了,如果指定了返回某個(gè)局部變量,那么這個(gè)變量就已經(jīng)被定義,那么在函數(shù)體內(nèi)即可直接使用。

    - 不指定返回變量名稱,那么需要在函數(shù)尾部寫(xiě)入 return 變量1,變量2, 如果指定了返回的變量名,那么只需要寫(xiě)上return即可。

    - 傳入的參數(shù)個(gè)數(shù),也可以不定(不定長(zhǎng)變參),使用...來(lái)表示,在函數(shù)體內(nèi)存儲(chǔ)這些數(shù)據(jù)的類型為slice

    func A(a ...int)  -->...int必須放在最后

    - 如果傳入的值有1個(gè)string,有n個(gè)int,那么只能 fun A(b string, a ...int)這種形式接受

    - 如果傳入的參數(shù)是一個(gè)常規(guī)的int、string等類型的話,屬于值傳遞(默認(rèn)),即只是值得拷貝,而如果傳遞sllice,則是引用傳遞(其實(shí)slice也屬于值拷貝,只不過(guò),slice拷貝的是內(nèi)存地址。而直接修改內(nèi)存地址會(huì)影響源數(shù)據(jù))

    - 如果需要把int、string類型的值傳入并修改,那么就需要把這些類型的變量的內(nèi)存地址傳入

package main
import "fmt"
func main() {
    a := 2
    A(a)
    fmt.Println(a)
}
func A(a int) {
    i := 3
    fmt.Println(i)
}
結(jié)果:
3
2


把變量a的地址傳入到函數(shù)中

package main
import "fmt"
func main() {
    a := 2
    A(&a)    //&a表示取a的內(nèi)存地址
    fmt.Println(a)
}
func A(a *int) {    //定義指針類型,指向a的內(nèi)存地址
    *a = 3    //直接對(duì)內(nèi)存地址進(jìn)行賦值
    fmt.Println(*a)
}
結(jié)果:
3
3


參數(shù)傳遞(傳值與傳指針)

    函數(shù)的參數(shù)傳遞分為兩種,值傳遞,和引用傳遞,值傳遞指在調(diào)用函數(shù)時(shí)將實(shí)際參數(shù)復(fù)制一份傳遞到函數(shù)中,這樣在函數(shù)中如果對(duì)參數(shù)進(jìn)行修改,將不會(huì)影響到實(shí)際參數(shù)。默認(rèn)情況下,GO是值傳遞,在調(diào)用過(guò)程中不會(huì)影響到實(shí)際參數(shù)。

    變量在內(nèi)存中是存放于一定的地址上的,修改變量實(shí)際是修改變量地址處的內(nèi)存。只有讓局部函數(shù)知道參數(shù)的內(nèi)存地址,才能修改變量的值。所以引用傳遞的時(shí)候需要把變量的內(nèi)存地址傳入到局部函數(shù)內(nèi)(&a,&表示傳遞變量的內(nèi)存地址),并將函數(shù)的參數(shù)類型調(diào)整為*int,即改為指針類型,才能在函數(shù)中修改變量的值,此時(shí)參數(shù)仍然是copy傳遞的,只不過(guò)copy的是一個(gè)指針。


函數(shù)作為其他變量的值

    在Go語(yǔ)言中,一切皆類型,函數(shù)也可以被命名為變量,然后對(duì)變量進(jìn)行函數(shù)的調(diào)用

package main
import "fmt"
func main() {
    a := A
    a()
}
func A() {
    fmt.Println("Func A")
}
結(jié)果:
Func A


匿名函數(shù)

    在定義函數(shù)的時(shí)候不指定函數(shù)的名稱,而是把函數(shù)直接賦值給某個(gè)變量的函數(shù)叫做匿名函數(shù),調(diào)用這個(gè)函數(shù)的時(shí)候,直接使用變量的名稱即可。(因?yàn)間olang中的func不支持函數(shù)嵌套,使用匿名函數(shù)可以達(dá)到嵌套的效果)   匿名函數(shù)不能作為頂級(jí)函數(shù)(最外層)

package main
import "fmt"
func main() {
    a := func() {
    fmt.Println("Func")
}
    a()
}


閉包函數(shù)

    所謂閉包函數(shù)就是將整個(gè)函數(shù)的定義一氣呵成寫(xiě)好并賦值給一個(gè)變量。然后用這個(gè)變量名作為函數(shù)名去調(diào)用函數(shù)體。閉包函數(shù)對(duì)它外層的函數(shù)中的變量具有訪問(wèn)和修改的權(quán)限

package main
import "fmt"
func main() {
    f := closure(10)    //調(diào)用閉包函數(shù)并傳遞10
    fmt.Println(f(1))    //傳遞1給返回的函數(shù),10+1=11
    fmt.Println(f(2))    //傳遞2給返回的函數(shù),10+2=12
}
func closure(x int) func(int) int {   //定義一個(gè)函數(shù)接收一個(gè)參數(shù)x,返回值也是一個(gè)函數(shù)接收一個(gè)變量y
    return func(y int) int {    //返回一個(gè)int,函數(shù)接收一個(gè)參數(shù),返回x+y的值
        return x + y
    }
}
結(jié)果:
11
12


defer

    - 執(zhí)行方式類似其他語(yǔ)言中的析構(gòu)函數(shù),在函數(shù)體執(zhí)行結(jié)束后按照調(diào)用順序的相反順序逐個(gè)執(zhí)行 (類似于棧的方式,先進(jìn)后出,后進(jìn)先出)

    - 即使函數(shù)發(fā)生了嚴(yán)重錯(cuò)誤也會(huì)執(zhí)行

    - 支持匿名函數(shù)的調(diào)用

    - 常用語(yǔ)資源清理、文件關(guān)閉、解鎖以及記錄時(shí)間等操作

    - 通過(guò)與匿名函數(shù)配合可在return之后修改函數(shù)計(jì)算結(jié)果

    - 如果函數(shù)體內(nèi)某個(gè)變量作為defer時(shí)匿名函數(shù)的參數(shù),則在定義defer時(shí)已經(jīng)獲得了拷貝,否則則是引用某個(gè)變量的地址

    - Go沒(méi)有異常機(jī)制,但有panic/recover模式來(lái)處理錯(cuò)誤

    - Panic可以再任何地方引發(fā),但recover只有在defer調(diào)用的函數(shù)中有效

例子

package main
import "fmt"
func main() {
    fmt.Println("a")
    defer fmt.Println("1")
    defer fmt.Println("2")
    defer fmt.Println("3")
}
結(jié)果:
a
3
2
1
可以看到,在程序執(zhí)行完畢后,defer是從最后一條語(yǔ)句開(kāi)始執(zhí)行的,證明了defer類似棧的運(yùn)行方式


defer搭配循環(huán)的結(jié)果

package main
import "fmt"
func main() {
    for i := 0; i < 3; i++ {
    defer fmt.Println(i)
    }
}
結(jié)果:
2
1
0


panic/recover實(shí)例

主要用來(lái)對(duì)程序的控制,并且僅針對(duì)函數(shù)級(jí)別的錯(cuò)誤進(jìn)行收集與回調(diào)。使程序能繼續(xù)運(yùn)行下去

package main
import "fmt"
func main() {
    A()
    B()
    C()
}
func A() {
    fmt.Println("A")
}
func B() {
    defer func() {    //這里定義defer執(zhí)行一個(gè)匿名函數(shù),用于捕捉panic,這里如果把defer放在panic之后那么程序執(zhí)行到panic后就會(huì)崩潰,那么defer就不會(huì)生效
        if err := recover(); err != nil {    //對(duì)引發(fā)的panic進(jìn)行判斷,由于手動(dòng)觸發(fā)了panic并發(fā)送了信息,那么用recover接收的異常返回值就要不為空,如果為nil表示沒(méi)有異常,不為nil就表示異常了,這里對(duì)recover的返回值進(jìn)行判斷
    }
}()
    panic("this is painc")//發(fā)送異常,異常信息為”this is panic“
}
func C() {
    fmt.Println("C")
}
結(jié)果:
A
C
由于在函數(shù)B中定義了異常的recover機(jī)制,所以不會(huì)迫使程序退出,會(huì)繼續(xù)執(zhí)行

panic/recover 實(shí)例2

package main
import "fmt"
func main() {
    fmt.Println("1")
    fmt.Println("2")
    f := func() {
        defer func() {
            if err := recover(); err != nil {
            fmt.Println("panic")
            }
        }()
    panic("hello world")
    fmt.Println("7")
    }

    f()
    fmt.Println("8")
    }
結(jié)果:
1
2
panic      //打印panic說(shuō)明程序已經(jīng)成功的捕捉到了異常
8


    定義了匿名函數(shù),并賦值給了變量f,匿名函數(shù)中的"7"不會(huì)打印,因?yàn)閳?zhí)行到panic已經(jīng)崩潰了,而我們?cè)谀涿瘮?shù)內(nèi)定義了recover捕捉,所以匿名函數(shù)會(huì)被退出,然后繼續(xù)執(zhí)行其他程序


擴(kuò)展:

    在go語(yǔ)言中是沒(méi)有異常捕獲機(jī)制的,通過(guò)panic/recover來(lái)實(shí)現(xiàn)錯(cuò)誤的捕獲以及處理,利用go函數(shù)多返回值的概念,來(lái)進(jìn)行check,如果err等于nil表示沒(méi)有發(fā)生錯(cuò)誤,當(dāng)程序發(fā)生比較嚴(yán)重的錯(cuò)誤,嚴(yán)重到無(wú)法彌補(bǔ),比如索引越界,由于我們不能準(zhǔn)確的判斷元素的個(gè)數(shù),所以recover也沒(méi)有意義,所以說(shuō)這個(gè)時(shí)候就是一個(gè)panic。如果知道可能會(huì)索引越界,并且希望程序能從錯(cuò)誤中回復(fù)回來(lái),那么這時(shí)候就需要用到recover,一旦調(diào)用recover,系統(tǒng)就會(huì)認(rèn)為你需要從panic狀態(tài)恢復(fù)過(guò)來(lái),當(dāng)程序進(jìn)入panic狀態(tài),那么正常的程序?qū)⒉粫?huì)被執(zhí)行,那么需要定義defer來(lái)執(zhí)行recover(),defer不管在任何狀態(tài)下,都會(huì)執(zhí)行,只要把recover放在defer中,那么不管程序發(fā)生了怎樣的錯(cuò)誤,程序都會(huì)回復(fù)過(guò)來(lái),需要注意的是defer類似棧的模式,后進(jìn)先出。在可能發(fā)生panic的程序之前,預(yù)先定義defer,否則程序運(yùn)行到painc后直接崩潰了,這個(gè)時(shí)候他只會(huì)去檢查預(yù)先定義好的defer,而你放在panic之后,將會(huì)失效


例子1:

判斷奇偶數(shù)

package main
import "fmt"
func main() {
a := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}
    fmt.Println("the slice is ", a)
    fmt.Println("the odd is ", odd(a))
    fmt.Println("the even is ", even(a))
}
func odd(num []int) []int {
    var result []int
        for _, value := range num {
            if value%2 == 0 {
                result = append(result, value)
            }
        }
    return result
}
func even(num []int) []int {
    var result []int
        for _, value := range num {
            if value%2 == 0 {
                continue
        }
    result = append(result, value)
    }
return result
}


思路:分別對(duì)切片進(jìn)行過(guò)濾,偶數(shù)功能模塊過(guò)濾一遍,挑出偶數(shù),奇數(shù)功能模塊過(guò)濾一遍,挑出奇數(shù)。缺點(diǎn),模塊復(fù)用 性差。


判斷奇偶數(shù):

package main
import (
    "fmt"
)
type funcation func(int) bool
func odd(num int) bool {
    if num%2 == 0 {
        return false
    }
    return true
}
func even(num int) bool {
    if num%2 == 0 {
        return true
        }
    return false
}
func filter(slice []int, f funcation) []int {
    var result []int
        for _, value := range slice {
            if f(value) {
                result = append(result, value)
            }
        }
    return result
}
func main() {
    a := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
    fmt.Println("the slice is ", a)
    fmt.Println("the odd is ", filter(a, odd))
    fmt.Println("the even is ", filter(a, even))
}


思路:把判斷奇偶的功能模塊化,然后再通過(guò)一個(gè)模塊調(diào)奇偶判斷模塊,然后再用main函數(shù)組織,(使用func類型,進(jìn)行功能模塊的傳遞),有點(diǎn),結(jié)構(gòu)性強(qiáng),邏輯強(qiáng)。


向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI