天天看點

GO語言基礎(三):控制結構

控制結構:

一、if-else結構:

if 是用于測試某個條件(布爾型或邏輯型)的語句,如果該條件成立,則會執行 if 後由大括号括起來的代碼塊,否則就忽略該代碼塊繼續執行後續的代碼。

if condition {
	// do something	
}
           

如果存在第二個分支,則可以在上面代碼的基礎上添加 else 關鍵字以及另一代碼塊,這個代碼塊中的代碼隻有在條件不滿足時才會執行。if 和 else 後的兩個代碼塊是互相獨立的分支,隻可能執行其中一個。

if condition {
	// do something	
} else {
	// do something	
}
           

如果存在第三個分支,則可以使用下面這種三個獨立分支的形式:

if condition1 {
	// do something	
} else if condition2 {
	// do something else	
} else {
	// catch-all or default
}
           

else-if 分支的數量是沒有限制的,但是為了代碼的可讀性,還是不要在 if 後面加入太多的 else-if 結構。如果你必須使用這種形式,則把盡可能先滿足的條件放在前面。

即使當代碼塊之間隻有一條語句時,大括号也不可被省略(盡管有些人并不贊成,但這還是符合了軟體工程原則的主流做法)。

關鍵字 if 和 else 之後的左大括号 { 必須和關鍵字在同一行,如果你使用了 else-if 結構,則前段代碼塊的右大括号 } 必須和 else-if 關鍵字在同一行。這兩條規則都是被編譯器強制規定的。

非法的 Go 代碼:

if x{
}
else {	// 無效的
}
           

要注意的是,在你使用 gofmt 格式化代碼之後,每個分支内的代碼都會縮進 4 個或 8 個空格,或者是 1 個 tab,并且右大括号與對應的 if 關鍵字垂直對齊。

在有些情況下,條件語句兩側的括号是可以被省略的;當條件比較複雜時,則可以使用括号讓代碼更易讀。條件允許是符合條件,需使用 &&、|| 或 !,你可以使用括号來提升某個表達式的運算優先級,并提高代碼的可讀性。

一種可能用到條件語句的場景是測試變量的值,在不同的情況執行不同的語句,不過将在第 5.3 節講到的 switch 結構會更适合這種情況。

示例 booleans.go

package main
import "fmt"
func main() {
	bool1 := true
	if bool1 {
		fmt.Printf("The value is true\n")
	} else {
		fmt.Printf("The value is false\n")
	}
}
           

輸出:

The value is true

注意事項:這裡不需要使用 if bool1 == true 來判斷,因為 bool1 本身已經是一個布爾類型的值。

這種做法一般都用在測試 true 或者有利條件時,但你也可以使用取反 ! 來判斷值的相反結果,如:if !bool1 或者 if !(condition)。後者的括号大多數情況下是必須的,如這種情況:if !(var1 == var2)。

當 if 結構内有 break、continue、goto 或者 return 語句時,Go 代碼的常見寫法是省略 else 部分(另見第 5.2 節)。無論滿足哪個條件都會傳回 x 或者 y 時,一般使用以下寫法:

if condition {
	return x
}
return y
           

注意事項 不要同時在 if-else 結構的兩個分支裡都使用 return 語句,這将導緻編譯報錯 function ends without a return statement(你可以認為這是一個編譯器的 Bug 或者特性)。

if 可以包含一個初始化語句(如:給一個變量指派)。這種寫法具有固定的格式(在初始化語句後方必須加上分号):
if initialization; condition {
	// do something
}
           

示例:

val := 10
if val > max {
	// do something
}
           

二、switch 結構

相比較 C 和 Java 等其它語言而言,Go 語言中的 switch 結構使用上更加靈活。它接受任意形式的表達式:

switch var1 {
	case val1:
		...
	case val2:
		...
	default:
		...
}
           

變量 var1 可以是任何類型,而 val1 和 val2 則可以是同類型的任意值。類型不被局限于常量或整數,但必須是相同的類型;或者最終結果為相同類型的表達式。前花括号 { 必須和 switch 關鍵字在同一行。

您可以同時測試多個可能符合條件的值,使用逗号分割它們,例如:case val1, val2, val3。

每一個 case 分支都是唯一的,從上至下逐一測試,直到比對為止。( Go 語言使用快速的查找算法來測試 switch 條件與 case 分支的比對情況,直到算法比對到某個 case 或者進入 default 條件為止。)

一旦成功地比對到某個分支,在執行完相應代碼後就會退出整個 switch 代碼塊,也就是說您不需要特别使用 break 語句來表示結束。

是以,程式也不會自動地去執行下一個分支的代碼。如果在執行完每個分支的代碼後,還希望繼續執行後續分支的代碼,可以使用 fallthrough 關鍵字來達到目的。

是以:

switch i {
	case 0: // 空分支,隻有當 i == 0 時才會進入分支
	case 1:
		f() // 當 i == 0 時函數不會被調用
}
           

并且:

switch i {
	case 0: fallthrough
	case 1:
		f() // 當 i == 0 時函數也會被調用
}
           

在 case …: 語句之後,您不需要使用花括号将多行語句括起來,但您可以在分支中進行任意形式的編碼。當代碼塊隻有一行時,可以直接放置在 case 語句之後。

您同樣可以使用 return 語句來提前結束代碼塊的執行。當您在 switch 語句塊中使用 return 語句,并且您的函數是有傳回值的,您還需要在 switch 之後添加相應的 return 語句以確定函數始終會傳回。

可選的 default 分支可以出現在任何順序,但最好将它放在最後。它的作用類似與 if-else 語句中的 else,表示不符合任何已給出條件時,執行相關語句。

示例 switch1.go:

package main

import "fmt"

func main() {
	var num1 int = 100

	switch num1 {
	case 98, 99:
		fmt.Println("It's equal to 98")
	case 100: 
		fmt.Println("It's equal to 100")
	default:
		fmt.Println("It's not equal to 98 or 100")
	}
}
           

輸出:

It's equal to 100
           

我們也可以使用 switch 語句判斷從鍵盤輸入的字元(詳見第 12.2 節的 switch.go)。switch 語句的第二種形式是不提供任何被判斷的值(實際上預設為判斷是否為 true),然後在每個 case 分支中進行測試不同的條件。當任一分支的測試結果為 true 時,該分支的代碼會被執行。這看起來非常像鍊式的 if-else 語句,但是在測試條件非常多的情況下,提供了可讀性更好的書寫方式。

switch {
	case condition1:
		...
	case condition2:
		...
	default:
		...
}
           

例如:

switch {
	case i < 0:
		f1()
	case i == 0:
		f2()
	case i > 0:
		f3()
}
           

任何支援進行相等判斷的類型都可以作為測試表達式的條件,包括 int、string、指針等。

示例 switch2.go:

package main

import "fmt"

func main() {
	var num1 int = 7

	switch {
	    case num1 < 0:
		    fmt.Println("Number is negative")
	    case num1 > 0 && num1 < 10:
		    fmt.Println("Number is between 0 and 10")
	    default:
		    fmt.Println("Number is 10 or greater")
	}
}
           

輸出:

Number is between 0 and 10
           

switch 語句的第三種形式是包含一個初始化語句:

switch initialization {
	case val1:
		...
	case val2:
		...
	default:
		...
}
           

這種形式可以非常優雅地進行條件判斷:

switch result := calculate(); {
	case result < 0:
		...
	case result > 0:
		...
	default:
		// 0
}
           

在下面這個代碼片段中,變量 a 和 b 被平行初始化,然後作為判斷條件:

switch a, b := x[i], y[j]; {
	case a < b: t = -1
	case a == b: t = 0
	case a > b: t = 1
}
           

三、for 結構

檔案 for1.go 中示範了最簡單的基于計數器的疊代,基本形式為:

for 初始化語句; 條件語句; 修飾語句 {}

示例 for1.go:

package main

import "fmt"

func main() {
	for i := 0; i < 5; i++ {
		fmt.Printf("This is the %d iteration\n", i)
	}
}
           

輸出:

This is the 0 iteration
This is the 1 iteration
This is the 2 iteration
This is the 3 iteration
This is the 4 iteration
           

由花括号括起來的代碼塊會被重複執行已知次數,該次數是根據計數器(此例為 i)決定的。循環開始前,會執行且僅會執行一次初始化語句

i := 0

;這比在循環之前聲明更為簡短。緊接着的是條件語句

i < 5

,在每次循環開始前都會進行判斷,一旦判斷結果為 false,則退出循環體。最後一部分為修飾語句

i++

,一般用于增加或減少計數器。

for 結構的第二種形式是沒有頭部的條件判斷疊代(類似其它語言中的 while 循環),基本形式為:for 條件語句 {}。您也可以認為這是沒有初始化語句和修飾語句的 for 結構,是以

;;

便是多餘的了。

package main

import "fmt"

func main() {
	var i int = 5

	for i >= 0 {
		i = i - 1
		fmt.Printf("The variable i is now: %d\n", i)
	}
}
           

for-range 結構

這是 Go 特有的一種的疊代結構,您會發現它在許多情況下都非常有用。它可以疊代任何一個集合(包括數組和 map,詳見第 7 和 8 章)。文法上很類似其它語言中 foreach 語句,但您依舊可以獲得每次疊代所對應的索引。

要注意的是,val 始終為集合中對應索引的值拷貝,是以它一般隻具有隻讀性質,對它所做的任何修改都不會影響到集合中原有的值(譯者注:如果 val 為指針,則會産生指針的拷貝,依舊可以修改集合中的原值)。一個字元串是 Unicode 編碼的字元(或稱之為 rune)集合,是以您也可以用它疊代字元串:

for pos, char := range str {
...
}
           

四、Break 與 continue

for {
	i = i - 1
	fmt.Printf("The variable i is now: %d\n", i)
	if i < 0 {
		break
	}
}
           

多層循環也可以

break+number

進行多層break

關鍵字 continue 忽略剩餘的循環體而直接進入下一次循環的過程,但不是無條件執行下一次循環,執行之前依舊需要滿足循環的判斷條件。

五、标簽與 goto

for、switch 或 select 語句都可以配合标簽(label)形式的辨別符使用,即某一行第一個以冒号(:)結尾的單詞(gofmt 會将後續代碼自動移至下一行)。

package main

import "fmt"

func main() {

LABEL1:
	for i := 0; i <= 5; i++ {
		for j := 0; j <= 5; j++ {
			if j == 4 {
				continue LABEL1
			}
			fmt.Printf("i is: %d, and j is: %d\n", i, j)
		}
	}

}
           

fcontinue 語句指向 LABEL1,當執行到該語句的時候,就會跳轉到 LABEL1 标簽的位置。

您可以看到當 j4 和 j5 的時候,沒有任何輸出:标簽的作用對象為外部循環,是以 i 會直接變成下一個循環的值,而此時 j 的值就被重設為 0,即它的初始值。如果将 continue 改為 break,則不會隻退出内層循環,而是直接退出外層循環了。另外,還可以使用 goto 語句和标簽配合使用來模拟循環。

package main

func main() {
	i:=0
	HERE:
		print(i)
		i++
		if i==5 {
			return
		}
		goto HERE
}
           

輸出:

01234

特别注意 使用标簽和 goto 語句是不被鼓勵的:它們會很快導緻非常糟糕的程式設計,而且總有更加可讀的替代方案來實作相同的需求。

如果您必須使用 goto,應當隻使用正序的标簽(标簽位于 goto 語句之後),但注意标簽和 goto 語句之間不能出現定義新變量的語句,否則會導緻編譯失敗。