本文始發於我的公衆號:TechFlow,原創不易,求個關注golang
今天是Golang專題的第四篇,這一篇文章將會介紹golang當中的函數、循環以及選擇判斷的具體用法。web
在以前的文章當中其實咱們已經接觸過函數了,由於咱們寫的main函數本質上也是一個函數。只不過因爲main函數沒有返回值,也沒有傳參,因此省略了不少信息。數組
func main() {
fmt.Println("Hello World")
}
複製代碼
下面,咱們來看看一個完整的函數是怎樣的,這是golang官網上的例子。數據結構
func add(x int, y int) int {
return x + y
}
複製代碼
這是一個很是簡單的a+b的函數,我想你們應該都能看懂。咱們來重點關注一下函數的格式。首先是func關鍵字,咱們使用這個關鍵字定義一個函數,以後跟着的是函數名,而後是函數的傳參,最後是函數的返回值。app
這個順序可能和咱們以前廣泛接觸的語法不太同樣,例如C++當中是把函數返回類型寫在最前面,而後是函數名和傳參。再好比Python當中則是沒有返回值的任何信息,只有def關鍵字和函數名以及傳入的參數。編輯器
golang有些像是Python和C++的綜合體,整體來講我以爲內涵上更接近C++,可是寫法上和Python更接近一些。分佈式
咱們理解了函數的定義以後,下面來看看golang當中支持的一些特性。函數
在變量聲明的時候,咱們若是定義兩個相同類型的變量是能夠把它們進行縮寫的。好比咱們定義兩個int類型的變量,分別叫作a和b。那麼能夠簡寫成這樣:優化
var a, b int
複製代碼
一樣,在函數當中,若是傳入的參數類型相同,也同樣是能夠簡寫的。咱們能夠把x和y兩個參數縮寫在一塊兒,用逗號分開,共享變量類型。編碼
func add(x, y int) int {
return x + y
}
複製代碼
在前面介紹golang特性的時候曾經提到過,golang做爲一個看起來很守舊的語言,可是卻支持不少新鮮的特性。其中最知名的一個特性就是函數支持多值返回,即便是如今,也只有少許的語言支持這一特性。
在許多語言當中,若是須要返回多個值,每每須要用一個結構體或者是tuple、list等數據結構將它們包裝起來。可是在golang當中支持同時返回多個結果,這將會極大地方便咱們的編碼。
func sample() (string, string) {
return "sample1", "sample2"
}
複製代碼
多值返回也會有一個小小的問題,就是若是咱們要返回的值過多,會致使這個return會寫得很長,或者是組裝的邏輯變得很複雜。或者是很容易產生遺漏、搞混順序之類的問題,golang當中針對這個問題也進行優化,支持咱們對返回值進行命名。當命名的變量賦值完成以後,咱們就能夠直接用return關鍵字返回全部數據。
這個操做很難用語言描述很清楚,咱們來看下面的例子:
func sample(x, y, z int) (xPrime, yPrime, zPrime int) {
xPrime, yPrime, zPrime = x-1, y+1, z-2
return
}
複製代碼
在上面的代碼當中,在返回以前,咱們先給要返回的值起好了名字,咱們在函數體當中對這些值進行賦值完成以後,咱們就能夠直接return了,golang會自動將它們的值填充進行返回。這樣不但能夠簡化必定的編碼過程,也能夠增長可讀性。
golang的函數當中有一個特殊的用法,就是defer。這個用法聽說其餘語言也有,可是我暫時沒有見到過。defer是一個關鍵字,用它修飾的語句會被存入棧中,直到函數退出的時候執行。
好比:
func main() {
defer fmt.Println("world")
fmt.Println("hello") } 複製代碼
上面這兩行代碼雖然defer的那一行在先,可是並不會被先執行,而是等main函數執行退出以前纔會執行。
看起來這個用法有一點點怪,可是它的用處很大,常常用到。好比當咱們打開一個文件的時候,無論文件有沒有打開成功,咱們都須要記得關閉文件。但若是文件打開不成功可能就會有異常或者是報錯,若是咱們把這些狀況所有都考慮到,會變得很是複雜。因此這個時候咱們一般都會用defer來執行文件的關閉。
要注意的是,defer修飾的代碼會被放入棧中。因此最後會按照先進後出的原則進行執行。好比:
func main() {
for i := 0; i < 10; i++ {
defer fmt.Println(i)
}
fmt.Println("done") } 複製代碼
最後執行的結果是9876543210,而不是相反。這一點蠻重要的,有的時候若是搞混了,很容易出現問題。
和其餘語言不一樣,Golang當中只有一種循環,就是for循環。沒有while,更沒有do while循環。在golang的設計中設想當中,只須要一種循環,就能夠實現全部的功能。從某種程度上來講,也的確如此,golang中的循環有點像是C++和Python循環的結合體,集合兩種所長。
首先,咱們先來看下for循環的語法,在for循環當中,咱們使用分號分開循環條件。循環條件分爲三個部分,第一個部分是初始化部分,咱們對循環體進行初始化,第二個部分是判斷部分,判斷循環結束的終止條件,第三個部分是循環變量的改變部分。
寫出來大概是這樣的:
for i := 0; i < 10; i++ {
fmt.Println(i)
}
複製代碼
這個語法是否是和C++中的循環很像呢?能夠說除了沒有括號以外,基本上就是同樣的。golang當中一樣支持++的自增操做,不過golang中只支持i++,而不支持++i。
和C++同樣,這三段當中的任何一段都是能夠省略的,好比咱們能夠省略判斷條件:
for i := 0; ; i++ {
fmt.Println(i)
if i > 10 {
break
}
}
複製代碼
咱們也能夠省略循環變量的自增條件:
for i := 0; i < 10; {
i += 2
fmt.Println(i)
}
複製代碼
甚至能夠所有省略,若是所有省略的話,等價於C++中的while(true)循環,也就是死循環。
若是咱們用循環遍歷一個數組或者是map,它的這個用法和Python中的用法很是相似。咱們來看下,假如咱們有一個數組是:
nums := []int{2, 3, 4}
sum := 0
for i, v := range nums {
sum += v
fmt.Println(i)
}
複製代碼
這個用法等價於Python中的for i, v in enumerate(nums)。也就是經過range會同時返回數組和map中的下標與對應的值,咱們再來看下map,其實也是同樣的。
kvs := map[string]string{"a": "apple", "b": "banana"}
for k, v := range kvs {
fmt.Printf("%s -> %s\n", k, v)
}
複製代碼
若是你看不懂map和數組的定義沒有關係,咱們會在以後的文章當中再來詳細講解,這篇的主要內容是循環。咱們只須要看得懂for循環的range操做便可。
golang當中支持if與switch進行條件判斷。咱們先來看if,在golang當中的if和Python比較接近,在if的判斷條件外面不須要加上小括號(),可是if的執行條件當中必需要大括號{},即便只有一行代碼。
好比剛纔咱們寫的循環中的那個break。
for i := 0; ; i++ {
fmt.Println(i)
if i > 10 {
break
}
}
複製代碼
上面的邏輯在各個語言中都大同小異,不少語言都是這麼寫的。可是golang對於if還有特殊的支持,golang支持在if條件當中加上初始化信息。
好比:
if v := sample(); v < 10 {
fmt.Println(v)
}
複製代碼
上面當中的v是在if執行的時候才進行的初始化,也就是說咱們將變量的初始化和if判斷結合在了一塊兒。這個用法很是重要,在golang當中也大規模使用,因此咱們必定要學會這個用法。
golang當中也支持switch用法,它的基本套路和C++同樣,可是在細微的地方又作了優化。
好比和if同樣,switch也支持在執行的時候初始化。好比:
switch flag := sample(); flag {
case "a":
fmt.Println(flag)
case "b":
fmt.Println(flag)
default:
fmt.Println(flag)
}
複製代碼
看明白了嗎,代碼當中的flag是咱們執行switch的時候才建立出來的。分號以前的都是初始化的代碼,分號以後的表達式纔是switch進行判斷的內容。
還有一個小細節須要注意,在golang當中使用switch的時候,每一個case的判斷條件後面不須要再加上break。咱們在寫其餘語言的時候,若是用到switch要麼就是忘記了case的執行條件後面要加上break,要麼就是寫不少break很是麻煩。golang的設計者以爲每一個case都加上break太二了,由於你們基本上都只用switch執行一個case,因此就去掉了必需要加上break這個設定。
在golang當中,switch的判斷條件按照順序執行。
爲何要強調這個呢?由於你頗有可能會看到有些人的代碼裏的switch沒有判斷條件,好比:
switch a := sample();{
case a < 5:
fmt.Println(a)
case a > 5:
fmt.Println(a)
default:
fmt.Println("end")
}
複製代碼
在上面這段代碼當中,咱們根本沒有爲switch設置判斷的根據,這段邏輯徹底等同於若干個if-else條件的羅列,它在golang當中一樣是容許的。
今天原本是分佈式專題,但實在是沒有想到什麼很好的題目,我也不喜歡強求,乾脆就換個主題吧。之後分佈式專題還會更新,不過可能要改爲間歇式的了,後面想少寫點理論,可以分享一點能夠實際用上的東西(因此須要的時間比較久)。
不知道你們從今天的內容當中有沒有感覺到golang這門語言的個性,不少地方看起來中規中矩,卻又能創造出新的用法來,至少我是很佩服設計者的想法的。golang當中這些新特性初見的時候每每會以爲不喜歡和排斥,怎麼看怎麼怪異,可是寫多了以後仍是蠻香的。
今天的文章就到這裏,掃碼關注,獲取更多優質文章。