Go的流程控制主要包括條件分支、循環和併發。
if語句通常由if關鍵字、條件表達式和由花括號包裹的代碼塊組成。在Go中,代碼塊必須由花括號包裹。這裏的條件表達式是結果類型爲bool的表達式。例:java
if number > 10 { number += 3 }
else分支:算法
if number > 10 { } else { }
if語句還支持串聯數組
if number > 100 { } else if number < 100 { } else { }
上面的number變量能夠用單獨的語句來聲明也能夠直接加入到if子句中:併發
if number := 1; number > 100 { }
這裏的number := 1被叫作if語句的初始化子句。它應被放置在if關鍵字和條件表達式之間,並與前者由空格間隔,與後者由英文分號間隔。咱們在這裏使用了短變量聲明語句,即:在聲明變量number的同時爲它賦值。它的做用域僅在這條if語句所表明的代碼塊中。也能夠說,變量number對於該if語句以外的代碼來講是不可見的。
若是咱們在if語句以外聲明瞭number變量,那麼number := 1也是合法的。ui
var number int if number := 1; number > 100 { }
這種寫法有一個專有名詞:標識符的重聲明。只要對同一個標識符的兩次聲明各自所在的代碼塊之間存在包含的關係,就會造成對該標識符的重聲明。具體到這裏,第一次聲明的number變量所在的是該if語句的外層代碼塊,而number := 1所聲明的number變量所在的是該if語句的代碼塊。它們之間存在包含關係,因此對number的重聲明就造成了。
這種狀況形成的結果就是,if語句內部對number的訪問和賦值都只會涉及到第二次聲明的那個number變量。這種現象叫作標識符的遮蔽。編碼
switch語句提供了一個多分支條件執行的方法。每個case能夠攜帶一個表達式或一個類型說明符。前者又可被簡稱爲case表達式。所以,Go語言的switch語句又分爲表達式switch語句和類型switch語句。spa
var name string
...
switch name { case "Golang": fmt.Println("Golang") case "Rust": fmt.Println("Rust") default: fmt.Println("PHP是世界上最好的語言") }
Go會依照從上至下的順序對每一條case語句中case表達式進行求值,只要被發現其表達式與switch表達式的結果相同,該case語句就會被選中。其他的case語句會被忽略。 與if相同,switch語句還能夠包含初始化字句,且其出現位置和寫法一模一樣:3d
names := []string{"Golang","java","PHP"} switch name:=names[0];name { case "Golang": fmt.Println("Golang") ... default: fmt.Println("Unknown") }
類型switch語句與通常形式有兩點差異。第一點,緊隨case關鍵字的不是表達式,而是類型說明符。類型說明符由若干個類型字面量組成,且多個類型字面量之間由英文逗號分隔。第二點,它的switch表達式是很是特殊的。這種特殊的表達式也起到了類型斷言的做用,但其表現形式很特殊,如:v.(type),其中v必須表明一個接口類型的值。該類表達式只能出如今類型switch語句中,且只能充當switch表達式。一個類型switch語句的示例以下:指針
v := 11 switch i := interface{}(v).(type) { case int, int8, int16, int32, int64: fmt.Println("A signed integer:%d. The type is %T. \n", v, i) case uint, uint8, uint16, uint32, uint64: fmt.Println("A unsigned integer: %d. The type is %T. \n", v, i) default: fmt.Println("Unknown!") }
咱們這裏把switch表達式的結果賦給了一個變量。如此以來,咱們就能夠在該switch語句中使用這個結果了。這段代碼被執行後,輸出:"A signed integer:11. The type is int."
最後說一下fallthrough。它既是一個關鍵字,又能夠表明一條語句。fallthrough語句可被包含在表達式switch語句中的case語句中。它的做用是使控制權流轉到下一個case。不過要注意fallthrough語句僅能做爲case語句中的最後一條語句出現。而且,包含它的case語句不是其所屬switch語句的最後一條case語句。code
一條for語句一般由關鍵詞for、初始化字句、條件表達式、後置子句和以花括號包裹的代碼塊組成。其中,初始化字句、條件表達式和後置子句之間需用分號分隔。例:
for i := 0; i < 10; i++ { fmt.Print(i, " ") }
初始化子句、條件表達式、後置子句中的任何一個或多個,不過起到分隔做用的分號通常須要被保留下來。除非在僅有條件表達式或三者全被省略時分號才能夠被一同省略。
能夠把上述的初始化子句、條件表達式、後置子句合稱爲for子句。for語句還有另一種編寫方式,那就是用range字句替換掉for字句。range子句包含一個或兩個迭代變量(用於與迭代出的值綁定)、特殊標記:=或=、關鍵字range以及range表達式。其中,range表達式的結果值的類型應該是可以被迭代的,包括:字符串類型、數組類型、數組的指針類型、切片類型、字典類型和通道類型。例:
for i, v := range "Go語言" { fmt.Printf("%d: %c\n", i, v) }
對於字符串類型的被迭代值來講,for語句每次會迭代出兩個值,第一個值表明第二個值在字符串中的索引,而第二個值則表明該字符串中的某一個字符。迭代是以索引遞增的順序進行的。上面的代碼輸出:
0: G 1: o 2: 語 5: 言
能夠看到迭代出的索引值並非連續的,字符串的底層是以字節數組的形式存儲的。而在Go中,字符串到字節數組的轉換是經過對其中的每一個字符進行UTF-8編碼來完成的。字符串"Go語言"中的每個字符與相應的字節數組之間的對應關係以下:
一箇中文字符在通過UTF-8編碼以後會表現爲三個字節。因此咱們用語[0],語[1]、語[2]分別表示字符'語'經編碼後的第1、2、三個字節。
這就能夠解釋爲何會打印出如上內容了:每次迭代出的第一個值所表明的是第二個字符值經編碼後的第一個字節在該字符串經編碼後的字節數組中的索引值。
對於數組值、數組的指針值和切片來講,range子句每次也會被迭代出兩個值。其中,第一個值會是第二個值在迭代值中的索引,而第二個值則是被迭代值中的某一個元素。一樣的,迭代是以索引遞增的順序進行的。
對於字典值來講,range子句每次仍然會迭代出兩個值。顯然,第一個值是字典中的某一個鍵,而第二個值則是該鍵對應的那個值。注意,對字典值上的迭代,Go語言是不保證其順序的。
攜帶range子句的for語句還能夠應用在一個通道值之上。其做用是不斷地從該通道值中接受數據,不過每次只會接收一個值。若是通道值中沒有數據,那麼for語句的執行會處於阻塞狀態。不管怎樣,這樣的循環會一直進行下去。直至該通道值被關閉,for語句的執行纔會結束。
select語句屬於條件分支流程控制語句,不過它只能用於通道。它能夠包含若干條case語句,並根據條件選擇其中之一執行。select語句的case關鍵詞只能後跟用於通道的發送操做的表達式以及接受操做的表達式或語句。例:
ch1 := make(chan int, 1) ch2 := make(chan int, 1) ... select { case e1 := <-ch1: fmt.Printf("1th case is selected. e1=%v.\n", e1) case e2 := <-ch2: fmt.Printf("2th case is selected. e2=%v.\n", e2) default: fmt.Printf("No data!") }
若是該select語句執行的時候通道ch1\ch2都沒有任何數據,那麼確定只有default case會被執行。可是,只要有一個通道在當時有數據就不會輪到default case執行了。顯然,對於包含通道接收操做的case來說,其執行條件就是通道中存在數據。若是在當時有數據的通道多於一個,那麼Go會經過一種僞隨機的算法來決定哪個case將被執行。
另外,對於包含通道發送操做的case來說,其執行條件就是通道中至少還能緩衝一個數據(或者說通道未滿)。相似,當有多個case中的通道未滿時,Go會隨機選擇:
ch3 := make(chan int, 100) ... select { case ch3 <- 1: fmt.Printf("Sent %d\n", 1) case ch3 <- 2: fmt.Printf("Send %d\n", 2) default: fmt.Println("Full channel!") }
該條select語句的兩個case中包含的都是針對通道ch3的發送操做。若是咱們把這條語句置於一個循環中,那麼就至關於用有限範圍的隨機整數集合去填滿一個通道。 **若是一個select語句中不存在default case,而且被執行時其中的全部case都不知足條件,那麼它的執行就會被阻塞!**當前進程的進行也會所以而停滯。直到其中一個case知足了執行條件,執行纔會繼續。咱們一直在說case 執行條件的知足與否取決於其操做的通道在當時的狀態。未被初始化的通道會使操做它的case永遠知足不了執行條件。 break語句也能夠被包含在select語句中的case語句中。它的做用是當即結束當前的select語句的執行。不論其所屬的case語句中是否還有未被執行的語句。