「每週譯 Go」如何在 Go 中編寫 Switch 語句
介紹
條件語句(點擊查看往期推文) 使程序員有能力指導他們的程序在某個條件爲真時採取某些行動,在條件爲假時採取另一種行動。經常,我們想把一些 變量 [1] 與多個可能的值進行比較,在每種情況下采取不同的行動。僅僅使用if
語句 [2] 就可以做到這一點。然而,編寫軟件不僅是爲了讓事情順利進行,也是爲了向未來的自己和其他開發者傳達你的意圖。switch
是一個替代性的條件語句,對於傳達你的 Go 程序在遇到不同選項時採取的行動很有用。
我們可以用 switch 語句編寫的所有內容也可以用if
語句編寫。在本教程中,我們將看幾個例子,看看 switch 語句能做什麼,它所取代的if
語句,以及它最合適的應用場合。
Switch 語句的結構
Switch 通常用於描述當一個變量被分配到特定值時程序所採取的行動。下面的例子演示了我們如何使用 if
語句來完成這個任務。
package main
import "fmt"
func main() {
flavors := []string{"chocolate", "vanilla", "strawberry", "banana"}
for _, flav := range flavors {
if flav == "strawberry" {
fmt.Println(flav, "is my favorite!")
continue
}
if flav == "vanilla" {
fmt.Println(flav, "is great!")
continue
}
if flav == "chocolate" {
fmt.Println(flav, "is great!")
continue
}
fmt.Println("I've never tried", flav, "before")
}
}
這將輸出如下信息:
chocolate is great!
vanilla is great!
strawberry is my favorite!
I've never tried banana before
在main
中,我們定義了一個 slice[3] 的冰激凌口味。然後我們使用一個for loop
(下週一推文會講到)來迭代它們。我們使用三個if
語句來打印不同的信息,表明對不同冰淇淋口味的偏好。每個if
語句必須使用continue
語句來停止for
循環的執行,這樣就不會在最後打印出首選冰淇淋口味的默認信息。
當我們添加新的偏好時,我們必須不斷添加if
語句來處理新的情況。重複的信息,如 "香草" 和 "巧克力" 的情況,必須有重複的if
語句。對於我們代碼的未來讀者(包括我們自己)來說,if
語句的重複性掩蓋了它們所做的重要部分 -- 將變量與多個值進行比較並採取不同的行動。另外,我們的回退信息與條件語句分開,使得它看起來不相關。轉換器 " 語句可以幫助我們更好地組織這個邏輯。
switch
語句以 switch
關鍵字開始,在其最基本的形式下,後面是一些要進行比較的變量。之後是一對大括號({}
),其中可以出現多個 case 子句。case 子句描述了當提供給 switch 語句的變量等於 case 子句所引用的值時,Go 程序應該採取的行動。下面的例子將先前的例子轉換爲使用一個switch
而不是多個if
語句:
package main
import "fmt"
func main() {
flavors := []string{"chocolate", "vanilla", "strawberry", "banana"}
for _, flav := range flavors {
switch flav {
case "strawberry":
fmt.Println(flav, "is my favorite!")
case "vanilla", "chocolate":
fmt.Println(flav, "is great!")
default:
fmt.Println("I've never tried", flav, "before")
}
}
}
輸出與之前相同:
chocolate is great!
vanilla is great!
strawberry is my favorite!
I've never tried banana before
我們再次在main
中定義了一片冰淇淋的口味,並使用range
語句來遍歷每個口味。但是這一次,我們使用了一個switch
語句來檢查flav
變量。我們使用兩個case'子句來表示偏好。我們不再需要
繼續'語句,因爲只有一個case
子句將被switch
語句執行。我們還可以將 "巧克力" 和 "香草" 條件的重複邏輯結合起來,在 case
子句的聲明中用逗號將其分開。default
子句是我們的萬能子句。它將對我們在 switch
語句中沒有考慮到的任何口味運行。在這種情況下,"香蕉" 將導致 default
的執行,打印出 "I've never tried banana before" 的信息。
這種簡化形式的switch
語句解決了它們最常見的用途:將一個變量與多個替代品進行比較。它還爲我們提供了便利,當我們想對多個不同的值採取相同的行動,以及在沒有滿足所列的條件時,通過使用所提供的default
關鍵字採取一些其他行動。
當這種簡化的switch
形式被證明太有侷限性時,我們可以使用一種更通用的switch
語句形式。
通常的 Switch 語句
switch
語句對於將更復雜的條件集合在一起以顯示它們之間有某種聯繫是很有用的。這在將某些變量與一定範圍的值進行比較時最常用,而不是像前面的例子中的特定值。下面的例子使用if
語句實現了一個猜謎遊戲,可以從switch
語句中受益:
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
rand.Seed(time.Now().UnixNano())
target := rand.Intn(100)
for {
var guess int
fmt.Print("Enter a guess: ")
_, err := fmt.Scanf("%d", &guess)
if err != nil {
fmt.Println("Invalid guess: err:", err)
continue
}
if guess > target {
fmt.Println("Too high!")
continue
}
if guess < target {
fmt.Println("Too low!")
continue
}
fmt.Println("You win!")
break
}
}
輸出將取決於所選擇的隨機數和你玩遊戲的程度。下面是一個例子會話的輸出:
Enter a guess: 10
Too low!
Enter a guess: 15
Too low!
Enter a guess: 18
Too high!
Enter a guess: 17
You win!
我們的猜謎遊戲需要一個隨機數來比較猜測的結果,所以我們使用math/rand
包中的rand.Intn
函數。爲了確保我們每次玩遊戲都能得到不同的target
值,我們使用rand.Seed
來根據當前時間隨機化隨機數發生器。rand.Intn
的參數100
將給我們一個 0-100 範圍內的數字。然後我們使用for
循環來開始收集玩家的猜測。
fmt.Scanf
函數爲我們提供了一種方法來讀取用戶的輸入到我們選擇的變量中。它接受一個格式化的字符串動詞,將用戶的輸入轉換爲我們期望的類型。這裏的%d
意味着我們期望一個 int
,我們傳遞 guess
變量的地址,這樣 fmt.Scanf
就能夠設置該變量。在 處理任何解析錯誤(點擊查看往期推文)之後,我們使用兩個if
語句來比較用戶的猜測和target
值。它們返回的string
和bool
一起控制顯示給玩家的信息,以及遊戲是否會退出。
這些 if
語句掩蓋了一個事實,即變量被比較的數值範圍都有某種聯繫。一眼就能看出我們是否遺漏了該範圍的某些部分,這也是很困難的。下一個例子重構了前面的例子,用switch
語句代替:
package main
import (
"fmt"
"math/rand"
)
func main() {
target := rand.Intn(100)
for {
var guess int
fmt.Print("Enter a guess: ")
_, err := fmt.Scanf("%d", &guess)
if err != nil {
fmt.Println("Invalid guess: err:", err)
continue
}
switch {
case guess > target:
fmt.Println("Too high!")
case guess < target:
fmt.Println("Too low!")
default:
fmt.Println("You win!")
return
}
}
}
這將產生類似以下的輸出:
Enter a guess: 25
Too low!
Enter a guess: 28
Too high!
Enter a guess: 27
You win!
在這個版本的猜謎遊戲中,我們用一個switch
語句代替了if
語句塊。我們省略了switch
的表達式參數,因爲我們只對使用switch
來收集條件語句感興趣。每個case
子句包含一個不同的表達式,將guess
與target
進行比較。與第一次用switch
代替if
語句類似,我們不再需要continue
語句,因爲只有一個case
子句會被執行。最後,default
子句處理guess == target
的情況,因爲我們已經用另外兩個case
子句覆蓋了所有其他可能的值。
在我們目前看到的例子中,正好有一個 case 語句將被執行。偶爾,你可能希望結合多個case
子句的行爲。switch
語句提供了另一個實現這種行爲的關鍵字。
Fallthrough
有時你想重複使用另一個 case
子句包含的代碼。在這種情況下,可以使用 fallthrough
關鍵字要求 Go 運行下一個 case
子句的主體。下面這個例子修改了我們之前的冰淇淋口味的例子,以更準確地反映我們對草莓冰淇淋的熱情:
package main
import "fmt"
func main() {
flavors := []string{"chocolate", "vanilla", "strawberry", "banana"}
for _, flav := range flavors {
switch flav {
case "strawberry":
fmt.Println(flav, "is my favorite!")
fallthrough
case "vanilla", "chocolate":
fmt.Println(flav, "is great!")
default:
fmt.Println("I've never tried", flav, "before")
}
}
}
將得到如下輸出:
chocolate is great!
vanilla is great!
strawberry is my favorite!
strawberry is great!
I've never tried banana before
正如我們之前看到的,我們定義了一個 string
片段來表示口味,並使用 for
循環來迭代。這裏的 switch
語句與我們之前看到的語句相同,但是在 case
子句的末尾添加了 fallthrough
關鍵字,即 strawberry
。這將使 Go 運行case "strawberry":
的主體,首先打印出字符串strawberry is my favorite!
。當它遇到fallthrough
時,它將運行下一個case
子句的主體。這將導致case "vanilla", "chocolate":
的主體運行,打印出strawberry is great!
。
Go 開發人員不經常使用fallthrough
關鍵字。通常情況下,通過使用fallthrough
實現的代碼重用,可以通過定義一個具有公共代碼的函數來更好地獲得。由於這些原因,一般不鼓勵使用fallthrough
。
總結
switch
語句幫助我們向閱讀代碼的其他開發者傳達出彼此有某種聯繫。使我們在將來添加新的情況時更容易添加不同的行爲,並有可能確保任何忘記的事情也能通過default
子句得到正確處理。下次你發現自己寫的多個if
語句都涉及同一個變量時,試着用switch
語句重寫它 -- 你會發現當需要考慮其他值時,它將更容易重寫。
相關鏈接:
[1]https://gocn.github.io/How-To-Code-in-Go/docs/11-How_To_Use_Variables_and_Constants_in_Go/#%E7%90%86%E8%A7%A3%E5%8F%98%E9%87%8F
[2]https://gocn.github.io/How-To-Code-in-Go/docs/23-How_To_Write_Conditional_Statements_in_Go/#if-%E8%AF%AD%E5%8F%A5
[3]https://gocn.github.io/How-To-Code-in-Go/docs/12-How_To_Convert_Data_Types_in_Go
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/Lh0InvVt05PMmVoNtUjCNg