《Go語言聖經》學習筆記 5 函式

2021-10-03 11:32:55 字數 4878 閱讀 9434

在go中,乙個函式可以返回多個值。

乙個函式內部可以將另乙個有多返回值的函式作為返回值。

可以將乙個返回多引數的函式作為該函式的引數。

如果乙個函式將所有的返回值都顯示的變數名,那麼該函式的return語句可以省略運算元。這稱之為bare return。

func

add(a, b, c int

)(d, e int

)

上述**中,如果d,e如果沒有被修改,那麼就返回零值。

很難保證乙個函式能夠沒有錯誤的執行,所以一定要將可能預見的錯誤進行返回。

如果導致失敗的原因只有乙個,額外的返回值可以是乙個布林值,通常被命名為ok。

處理錯誤的五種策略

執行中的錯誤有很多種,對於不同的錯誤應該採取不同的策略

最常用的方式是傳播錯誤。也就是從底層函式不斷的返回,並且在這個過程中給錯誤加上一些資訊,確保錯誤最後傳回main函式的時候能夠分析出錯誤從**傳導出來的。

偶然錯誤,進行重試。如果錯誤的發生是偶然性的,或由不可預知的問題導致的。乙個明智的選擇是重新嘗試失敗的操作。在重試時,我們需要限制重試的時間間隔或重試的次數,防止無限制的重試。

程式無法繼續執行,輸出錯誤資訊並結束程式。需要注意的是,這種策略只應在main中執行。對庫函式而言,應僅向上傳播錯誤,除非該錯誤意味著程式內部包含不一致性,即遇到了bug,才能在庫函式中結束程式。

有時,我們只需要輸出錯誤資訊就足夠了,不需要中斷程式的執行。我們可以通過log包提供函式

直接忽略掉錯誤。用於即便有錯誤也不會影響程式的整體情況。

函式像其他值一樣,擁有型別,可以被賦值給其他變數,傳遞給函式,從函式返回。

函式型別的零值是nil。呼叫值為nil的函式值會引起panic錯誤

函式值之間是不可比較的,也不能用函式值作為map的key。

舉個簡單有趣的例子

func

add(x, y int

)int

func

sub(x, y int

)int

func

op(x, y int

, f func

(int

,int

)int

)int

func

main()

在上面函式op中f就是乙個函式值。當然函式值也是可以像下面一樣操作的。

func

square

(n int

)int

func

negative

(n int

)int

func

product

(m, n int

)int

f := square

fmt.

println(f

(3))

// "9"

f = negative

fmt.

println(f

(3))

// "-3"

fmt.

printf

("%t\n"

, f)

// "func(int) int"

f = product // compile error: can't assign func(int, int) int to func(int) int

匿名函式從名字上的意義來看就是沒有名字的函式。它的乙個重要的應用場景就是閉包:比如函式a中定義了匿名函式b,並將匿名函式b做引數返回,其中b可以對a中的變數進行操作。所以在函式a外部接收到b,便可以通過b來對a中變數進行操作。

// squares返回乙個匿名函式。

// 該匿名函式每次被呼叫時都會返回下乙個數的平方。

func

squares()

func()

int}

func

main()

乙個更難但更有趣的閉包:

func

a(x int)(

func

(int

)int

,func

(int

)int

) b :=

func

(i int

)int

return a, b

}func

main()

捕獲迭代變數

慮這個樣乙個問題:你被要求首先建立一些目錄,再將目錄刪除。在下面的例子中我們用函式值來完成刪除操作。下面的示例**需要引入os包。為了使**簡單,我們忽略了所有的異常處理。

var rmdirs [

]func()

for_

, d :=

range

tempdirs()

)}// ...do some work…

for_

, rmdir :=

range rmdirs

引數數量可變的函式稱為為可變引數函式。

可變引數一定要在函式引數列表中的最後乙個。

func

dsum

(d int

, args ...

int)

intreturn d * result

}func

main()

fmt.

println

(dsum(2

, arr...))

// 30

// 對於陣列可以利用切片生成切片

arr1 :=

[...

]int

fmt.

println

(dsum(2

, arr1[:]

...)

)// 30

}

特性:

func

main()

}

結果為:2,1,0.符合上面所述特性的第一點

不過使用defer有乙個問題,就是上面所提到的最後一點,可以看看下面的程式,嘗試下想想可能會發生的結果:

func

main()

()}for i:=

0; i<

3; i++

(i)// 傳參

}}

結果:

210

333

首先,在結果中輸出的前三個變數是第二個for的結果,後面是三個是第乙個for的結果。(不明白的回去看上面的例子,結合特性的第一點)

第乙個for之所以會輸出3個3,是因為這裡產生了閉包,使用的i是func外的變數i,所以位址並沒有改變。

而第二個for中定義的函式其實自己宣告了變數,所以將外部的變數i傳參給func的時候是進行了值拷貝,所以func內部的i和外部的i是兩個位址。

分析一下下面函式,看看是不是和自己預期的一樣:

func

main()

for i:=

0; i<

4; i++()

fs[i]

=func()

}for

_, f :=

range fs

}

結果:

closure i =  4

closure i = 4

closure i = 4

closure i = 4

closure i = 4

defer i = 3

closure i = 4

defer i = 2

closure i = 4

defer i = 1

closure i = 4

defer i = 0

go語言中使用panic來觸發錯誤。一旦觸發panic,之後所有的操作都會暫停。

funca(

)funcb(

)funcc(

)func

main()

結果如下:可以看到c函式並不會被執行

a

bpanic: panic in b

在觸發panic函式之前有defer函式了,那麼被defer的函式能夠在觸發panic之後正常執行完。

例如將上面的函式b改寫為以下:

funcb(

)()panic

("panic in b"

)}

結果如下:

a

bfunc in b

panic: panic in b

可以看到defer的匿名函式會在panic之前執行,c函式並不會被執行

在程式設計中,我們能夠預想到一些錯誤,這些錯誤能通過處理後能夠保證程式繼續正確執行而不會中斷,那麼可以使用recover,使即便觸發panic程式依然能夠繼續執行。

繼續修改上面的函式b:

funcb(

)}()

panic

("panic in b"

)}

上述**中, err是panic的內容,整個程式執行結果如下:

a

brecover from panic: panic in b

c

可以看到程式恢復正常執行,並且執行完整個流程了。

《Go語言聖經》學習筆記 2 程式結構(上)

package main import fmt func main const i 100 可以將常量 變數 定義在最後面上面程式是可以正常執行並輸出正確結果的。一級型別 全域性變數,函式 都可以定義在後面。變數宣告語法 var 變數名字 型別 表示式使用方式如下 func main 當 中的變數沒...

日常 Go語言聖經 匿名函式習題

go語言聖經 匿名函式 1.擁有函式名的函式只能在包級語法塊中被宣告,通過函式字面量 function literal 我們可繞過這一限制,在任何表示式中表示乙個函式值 2.通過這種方式定義的函式可以訪問完整的詞法環境 lexical environment 這意味著在函式中定義的內部函式可以引用該...

日常 Go語言聖經 匿名函式習題

go語言聖經 匿名函式 1.擁有函式名的函式只能在包級語法塊中被宣告,通過函式字面量 function literal 我們可繞過這一限制,在任何表示式中表示乙個函式值 2.通過這種方式定義的函式可以訪問完整的詞法環境 lexical environment 這意味著在函式中定義的內部函式可以引用該...