Go基礎(chǔ)——function函數(shù)

函數(shù)的聲明

在 Go 語言中,函數(shù)聲明通用語法如下:

func 函數(shù)名稱( [參數(shù)列表] ) ([返回值列表])
{
  執(zhí)行語句
}

1.函數(shù)的聲明以關(guān)鍵詞 func開始 。

  1. 函數(shù)名和參數(shù)列表一起構(gòu)成了函數(shù)簽名。
  2. 參數(shù)列表定義在 () 之間,聲明一個參數(shù)的語法采用 參數(shù)名 參數(shù)類型 的方式,任意多個參數(shù)采用類似 (parameter1 type, parameter2 type) 即(參數(shù)1 參數(shù)1的類型,參數(shù)2 參數(shù)2的類型)的形式指定。參數(shù)是可選的,即函數(shù)可以不包含參數(shù)。參數(shù)就像一個占位符,這是參數(shù)被稱為形參,當(dāng)函數(shù)被調(diào)用時,將具體的值傳遞給參數(shù),這個值被稱為實際參數(shù)。
  3. Go函數(shù)支持多返回值。 函數(shù)可以有返回值也可以沒有。所以下面這個函數(shù)的聲明也是有效的:
func functionname() {  
}

我們以寫一個計算商品價格的函數(shù)為例,輸入?yún)?shù)是單件商品的價格和商品的個數(shù),兩者的乘積為商品總價,作為函數(shù)的輸出值。

func calculateBill(price int, no int) int {  
    var totalPrice = price * no // 商品總價 = 商品單價 * 數(shù)量
    return totalPrice // 返回總價
}

上述函數(shù)有兩個整型的輸入 priceno,返回值 totalPricepriceno 的乘積,也是整數(shù)類型。
如果有連續(xù)若干個參數(shù),它們的類型一致,那么我們無須一一羅列,只需在最后一個參數(shù)后添加該類型。*例如,price int, no int可以簡寫為 price, no int,所以示例函數(shù)也可寫成

func calculateBill(price, no int) int {  
    var totalPrice = price * no
    return totalPrice
}

現(xiàn)在我們已經(jīng)定義了一個函數(shù),我們要在代碼中嘗試著調(diào)用它。調(diào)用函數(shù)的語法為 functionname(parameters)。調(diào)用示例函數(shù)的方法如下:

calculateBill(10, 5)

完成了示例函數(shù)聲明和調(diào)用后,我們就能寫出一個完整的程序,并把商品總價打印在控制臺上:

func calculateBill(price, no int) int {  
    var totalPrice = price * no
    return totalPrice
}
func main() {  
    price, no := 90, 6 // 定義 price 和 no,默認(rèn)類型為 int
    totalPrice := calculateBill(price, no)
    fmt.Println("Total price is", totalPrice) // 打印到控制臺上
}

函數(shù)調(diào)用

如果函數(shù)和調(diào)用不在同一個包(package)內(nèi),需要先通過import關(guān)鍵字將包引入–import “fmt”。函數(shù)Println()就屬于包fmt。與其他語言不同的是在Go語言中函數(shù)名字的大小寫不僅僅是風(fēng)格,更直接體現(xiàn)了該函數(shù)的是私有函數(shù)還是公有函數(shù)。函數(shù)名首字母小寫為private,大寫為public。

package utiles

func Max(a,b int )(m int){
    if a>b {
        return a
    }
    return b
}

Max 函數(shù)如果被其他包調(diào)用 首字母需要大寫

import (
    "utiles"
    "fmt"
)

func min(a ,b int) (m int){
    if a>b {
        return b
    }
    return a
}

func main(){
   r := utiles.Max(2,4)
   d  := min(2,4)
   fmt.Println(r,d)
}

多個返回值

Go 語言支持一個函數(shù)可以有多個返回值。

import (
    "fmt"
)
func swap(a string,b string)(string,string){
    return b,a;
}

func main() {
    a,b :=swap("abc","def")
    fmt.Println(a,b)
}

如果調(diào)用方調(diào)用了一個具有多返回值的方法,但是卻不想關(guān)心其中的某個返回值,用一個下劃線 _ 來忽略這個返回值。如同一個占位符號,如果我們只關(guān)注第一個返回值則可以寫成:

a, _ := swap("hello", "world")

如果關(guān)注第二返回值則可以寫成:

_, b := swap("hello", "world")

函數(shù)參數(shù)

1.值傳遞:調(diào)用函數(shù)時將實際參數(shù)復(fù)制一份傳遞到函數(shù)中,這樣在函數(shù)中如果對參數(shù)進行修改,將不會影響到實際參數(shù)。
2.引用傳遞:調(diào)用函數(shù)時將實際參數(shù)復(fù)制一份傳遞到函數(shù)中,這樣在函數(shù)中如果對參數(shù)進行修改,將不會影響到實際參數(shù)。

import (
"fmt"

)
func changeValue(a string){
     a = "new value"
}

func change(a *string){
    *a = "new value2"
}

func main() {
    a := "old value"
    changeValue(a)
    fmt.Println(a)
    change(&a)
    fmt.Println(a)
    /*
    print result:
    old value     
    new value2
    */
}

返回值

從函數(shù)中可以返回一個命名值。一旦命名了返回值,可以認(rèn)為這些值在函數(shù)第一行就被聲明為變量了。命名返回值作為結(jié)果形參(result parameters)被初始化為相應(yīng)類型的零值,當(dāng)需要返回的時候,我們只需要一條簡單的不帶參數(shù)的return語句。

func getResult(input int) (a int, b int) {
    a = 2 * input
    b = 3 * input
    return
}
func main() {
    n,m:= getResult(2);
    fmt.Print(n,m)
}

函數(shù)中的 return 語句沒有顯式返回任何值。由于a 和 b 在函數(shù)聲明中指定為返回值, 因此當(dāng)遇到 return 語句時, 它們將自動從函數(shù)返回。

可變參數(shù)

Go函數(shù)支持不定數(shù)量的參數(shù)的。為了做到這點,首先需要定義函數(shù)使其接受變參,類型“…type“本質(zhì)上是一個數(shù)組切片,也就是[]type,

func funcName(arg ...type) {
}

arg ...type告訴Go這個函數(shù)接受不定數(shù)量的參數(shù)。在下面的例子中參數(shù)的類型全部是int。在函數(shù)體中,變量arg是一個int的slice.
在參數(shù)賦值時可以不用用一個一個的賦值,可以直接傳遞一個數(shù)組或者切片,特別注意的是在參數(shù)后加上“…”即可。

import (
    "fmt"
)

func min(a...int) int{
    if len(a)==0 {
        return 0

    }
    min := a[0]
    for _,v :=range a{
        if min >v {
            min = v
        }
    }
    return min;
}

func main() {
    n := min(7,12,3,9,8)
    arr := []int {45,27,60,67,18}
    r := min(arr...)
    m := min(arr[:3]...)
    fmt.Print(n,r,m)
}

那么如果函數(shù)的參數(shù)類型不一致需要使用interface{}

import (
    "fmt"
    "reflect"
)

func diffType(args ...interface{}){
    for _, arg :=range args {
        fmt.Println(arg)
        fmt.Println(reflect.TypeOf(arg))
    }
}
func main() {
    diffType("test",1,12.5,[5]byte{1,3,4})
}

defer

return 可以返回 函數(shù)執(zhí)行的結(jié)果值,通過關(guān)鍵字 defer 允許我們推遲到函數(shù)返回之前(或任意位置執(zhí)行 return 語句之后)一刻才執(zhí)行某個語句或函數(shù),這樣return不是單純地返回某個值,同樣可以包含一些操作。
注意:多個defer語句 遵循棧的特征:先進后出

func testDefer() {
    defer fmt.Println("hello")
    defer fmt.Println("hello v2")
    defer fmt.Println("hello v3")
    fmt.Println("aaaaa")
    fmt.Println("bbbb")
}
func testDefer2() {
    var i int = 0
    defer fmt.Printf("defer i=%d\n", i)
    i= 1000
    fmt.Printf("i=%d\n", i)
}

func main() {
     testDefer()
     testDefer2()
}

匿名函數(shù)

匿名函數(shù)由一個不帶函數(shù)名的函數(shù)聲明和函數(shù)體組成,比如:

func(x,y int) int {
    return x + y
}

Go中函數(shù)是值類型,即可以作為參數(shù),又可以作為返回值。
匿名函數(shù)可以賦值給一個變量:

f := func() int {
    ...
}

定義函數(shù)類型:

type CalcFunc func(x,y int ) int 

函數(shù)作為參數(shù):

func Add(x, y int) int {
    return x + y
}

func Sub(x, y int) int {
    return x - y
}

type CalcFunc func(x,y int ) int 

func Operation(x, y int, calcFunc CalcFunc) int {
    return calcFunc(x, y)
}

func main() {
    sum := Operation(1, 2, Add)
    difference := Operation(1, 2, Sub)
    fmt.Println(sum,difference)
}

函數(shù)可以作為返回值:

// 第一種寫法
func add(x, y int) func() int {
    f := func() int {
        return x + y
    }
    return f
}

// 第二種寫法
func add(x, y int) func() int {
    return func() int {
        return x + y
    }
}

閉包

避免程序運行時異常崩潰

Golang中對于一般的錯誤處理提供了error接口,對于不可預(yù)見的錯誤(異常)處理提供了兩個內(nèi)置函數(shù)panic和recover。error接口類似于C/C++中的錯誤碼,panic和recover類似于C++中的try/catch/throw。
當(dāng)在一個函數(shù)執(zhí)行過程中調(diào)用panic()函數(shù)時,正常的函數(shù)執(zhí)行流程將立即終止,但函數(shù)中之前使用defer關(guān)鍵字延遲執(zhí)行的語句將正常展開執(zhí)行,之后該函數(shù)將返回到調(diào)用函數(shù),并導(dǎo)致逐層向上執(zhí)行panic流程,直至所屬的goroutine中所有正在執(zhí)行的函數(shù)被終止。錯誤信息將被報告,包括在調(diào)用panic()函數(shù)時傳入的參數(shù),這個過程稱為異常處理流程。
recover函數(shù)用于終止錯誤處理流程。一般情況下,recover應(yīng)該在一個使用defer關(guān)鍵字的函數(shù)中執(zhí)行以有效截取錯誤處理流程。如果沒有在發(fā)生異常的goroutine中明確調(diào)用恢復(fù)過程(調(diào)用recover函數(shù)),會導(dǎo)致該goroutine所屬的進程打印異常信息后直接退出。

對于第三方庫的調(diào)用,在不清楚是否有panic的情況下,最好在適配層統(tǒng)一加上recover過程,否則會導(dǎo)致當(dāng)前進程的異常退出,而這并不是我們所期望的。
簡單的實現(xiàn)如下:

func thirdPartyAdaptedHandler(...) {
    defer func() {
        err := recover()
        if err != nil {
            fmt.Println("some exception has happend:", err)
        }
    }()
    ...
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容