理解Go流程控制與快樂路徑原則
有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
一、流程控制基本介紹
流程控制是每種編程語言控制邏輯走向和執(zhí)行次序的重要部分,流程控制可以說是一門語言的“經(jīng)脈”。
那么 Go 語言對分支與循環(huán)兩種控制結(jié)構(gòu)的支持是怎么樣的呢?針對程序的分支結(jié)構(gòu),Go 提供了 if
和 switch-case
兩種語句形式;我們就先從 Go 語言分支結(jié)構(gòu)之一的 if 語句開始講起。
二、if 語句
2.1 if 語句介紹
if 語句是 Go 語言中提供的一種分支控制結(jié)構(gòu),它也是 Go 中最常用、最簡單的分支控制結(jié)構(gòu)。它會根據(jù)布爾表達(dá)式的值,在兩個分支中選擇一個執(zhí)行。
2.2 單分支結(jié)構(gòu)的 if 語句形式
單分支結(jié)構(gòu)的if
語句包含一個條件表達(dá)式和一個要執(zhí)行的代碼塊。如果條件表達(dá)式的值為true
,則執(zhí)行代碼塊。如果條件表達(dá)式的值為false
,則代碼塊將被跳過。以下是單分支結(jié)構(gòu)的if
語句的一般形式:
if boolean_expression { // 新分支 } // 原分支
這個 if 語句中的代碼執(zhí)行流程就等價于下面這幅流程圖:
boolean_expression
是一個布爾表達(dá)式,通常返回true
或false
。- 如果
boolean_expression
的值為true
,則執(zhí)行// 當(dāng)條件為真時執(zhí)行的代碼
部分的代碼塊。 - 如果
boolean_expression
的值為false
,則代碼塊將被跳過,繼續(xù)執(zhí)行下一個語句。
2.3 Go 的 if 語句的特點(diǎn)
2.3.1 分支代碼塊左大括號與if同行
if
語句的分支代碼塊的左大括號與 if
關(guān)鍵字在同一行上,這是 Go
代碼風(fēng)格的統(tǒng)一要求,gofmt
工具會幫助我們實(shí)現(xiàn)這一點(diǎn);
2.3.2 條件表達(dá)式不需要括號
if 語句的布爾表達(dá)式整體不需要用括號包裹,這使得代碼更加簡潔。而且,if 關(guān)鍵字后面的條件判斷表達(dá)式的求值結(jié)果必須是布爾類型,即要么是 true
,要么是 false
:
if runtime.GOOS == "darwin" { println("we are on MacOS") }
如果判斷的條件比較多,我們可以用多個邏輯操作符連接起多個條件判斷表達(dá)式,比如這段代碼就是用了多個邏輯操作符 && 來連接多個布爾表達(dá)式:
if (runtime.GOOS == "darwin") && (runtime.GOARCH == "amd64") && (runtime.Compiler != "gccgo") { println("we are using standard go compiler on Mac os for amd64") }
上面示例代碼中的每個布爾表達(dá)式都被小括號括上了,這是為了降低你在閱讀和理解這段代碼時,面對操作符優(yōu)先級的心智負(fù)擔(dān)。
三、操作符
3.1 邏輯操作符
邏輯操作符除了上面的 && 之外,Go 還提供了另外兩個邏輯操作符,如下表:
3.2 操作符的優(yōu)先級
一元操作符,比如上面的邏輯非操作符,具有最高優(yōu)先級,其他操作符的優(yōu)先級如下:
優(yōu)先級(從高到低) | 操作符列表 |
---|---|
5 | *, /, %, <<, >>, &, &^ |
4 | +, - |
3 | !=, ==, <, <=, >, >= |
2 | && |
1 | \ |
- 優(yōu)先級5的是乘、除、取模和位操作符
- 優(yōu)先級4的是加法和減法運(yùn)算符
- 優(yōu)先級3的是關(guān)系和相等運(yùn)算符
- 優(yōu)先級2的是邏輯與
- 優(yōu)先級最低的是邏輯或
操作符優(yōu)先級決定了操作數(shù)優(yōu)先參與哪個操作符的求值運(yùn)算,我們以下面代碼中 if 語句的布爾表達(dá)式為例:
func main() { a, b := false,true if a && b != true { println("(a && b) != true") return } println("a && (b != true) == false") }
這段代碼會輸出得到的是 a && (b != true) == false
。這是為什么呢?
這段代碼的關(guān)鍵就在于,if 后面的布爾表達(dá)式中的操作數(shù) b 是先參與 && 的求值運(yùn)算,還是先參與!= 的求值運(yùn)算。根據(jù)前面的操作符優(yōu)先級表,我們知道,!= 的優(yōu)先級要高于 &&,因此操作數(shù) b 先參與的是!=
的求值運(yùn)算,這樣 if
后的布爾表達(dá)式就等價于 a && (b != true)
。
針對以上問題,推薦在 if 布爾表達(dá)式中,使用帶有小括號的子布爾表達(dá)式來清晰地表達(dá)判斷條件。
這樣做不僅可以消除了自己記住操作符優(yōu)先級的學(xué)習(xí)負(fù)擔(dān),當(dāng)其他人閱讀你的代碼時,也可以很清晰地看出布爾表達(dá)式要表達(dá)的邏輯關(guān)系,這能讓我們代碼的可讀性更好,更易于理解,不會因記錯操作符優(yōu)先級順序而產(chǎn)生錯誤的理解。
三、if 多(N)分支結(jié)構(gòu)
3.1 if else(分支結(jié)構(gòu))
Go語言中if else(分支結(jié)構(gòu))
條件判斷的格式如下:
if boolean_expression { // 分支1 } else { // 分支2 }
3.2 (N)分支結(jié)構(gòu)(if ... else if ... else)
if
條件(N)分支結(jié)構(gòu)格式如下:
if boolean_expression1 { // 分支1 } else if boolean_expression2 { // 分支2 ... ... } else if boolean_expressionN { // 分支N } else { // 分支N+1 }
我們以下面這個四分支的代碼為例,看看怎么拆解這個多分支結(jié)構(gòu):
if boolean_expression1 { // 分支1 } else if boolean_expression2 { // 分支2 } else if boolean_expression3 { // 分支3 } else { // 分支4 }
以下是一個示例,演示如何使用if-else
結(jié)構(gòu)來判斷一個分?jǐn)?shù)的等級:
package main import "fmt" func main() { score := 85 if score >= 90 { fmt.Println("A") } else if score >= 80 { fmt.Println("B") } else if score >= 70 { fmt.Println("C") } else { fmt.Println("D") } }
四、if 語句的自用變量
無論是單分支、二分支還是多分支結(jié)構(gòu),我們都可以在 if 后的布爾表達(dá)式前,進(jìn)行一些變量的聲明,在 if 布爾表達(dá)式前聲明的變量,叫 if 語句的自用變量。顧名思義,這些變量只可以在 if 語句的代碼塊范圍內(nèi)使用,比如下面代碼中的變量 a、b 和 c:
func main() { if a, c := f(), h(); a > 0 { println(a) } else if b := f(); b > 0 { println(a, b) } else { println(a, b, c) } }
我們可以看到自用變量聲明的位置是在每個 if 語句的后面,布爾表達(dá)式的前面,而且,由于聲明本身是一個語句,所以我們需要把它和后面的布爾表達(dá)式通過分號分隔開。
在 if 語句中聲明自用變量是 Go 語言的一個慣用法,這種使用方式直觀上可以讓開發(fā)者有一種代碼行數(shù)減少的感覺,提高可讀性。同時,由于這些變量是 if 語句自用變量,它的作用域僅限于 if 語句的各層隱式代碼塊中,if 語句外部無法訪問和更改這些變量,這就讓這些變量具有一定隔離性,這樣你在閱讀和理解 if 語句的代碼時也可以更聚焦。
五、if 語句的“快樂路徑”原則
上面我們已經(jīng)學(xué)了 if 分支控制結(jié)構(gòu)的三種形式了,從可讀性上來看,單分支結(jié)構(gòu)要優(yōu)于二分支結(jié)構(gòu),二分支結(jié)構(gòu)又優(yōu)于多分支結(jié)構(gòu)。那么顯然,我們在日常編碼中要減少多分支結(jié)構(gòu),甚至是二分支結(jié)構(gòu)的使用,這會有助于我們編寫出優(yōu)雅、簡潔、易讀易維護(hù)且不易錯的代碼。
首先,我們來看一段偽代碼段1:
//偽代碼段1: func doSomething() error { if errorCondition1 { // some error logic ... ... return err1 } // some success logic ... ... if errorCondition2 { // some error logic ... ... return err2 } // some success logic ... ... return nil }
我們看到單分支控制結(jié)構(gòu)的偽代碼段 1 有這幾個特點(diǎn):
- 沒有使用 else 分支,失敗就立即返回;
- “成功”邏輯始終“居左”并延續(xù)到函數(shù)結(jié)尾,沒有被嵌入到 if 的布爾表達(dá)式為 true 的代碼分支中;
- 整個代碼段布局扁平,沒有深度的縮進(jìn);
- 代碼的可讀性很高
我們來看一段偽代碼段2:
// 偽代碼段2: func doSomething() error { if successCondition1 { // some success logic ... ... if successCondition2 { // some success logic ... ... return nil } else { // some error logic ... ... return err2 } } else { // some error logic ... ... return err1 } }
偽代碼段 2 實(shí)現(xiàn)了同樣邏輯碼段 1,就使用了帶有嵌套的二分支結(jié)構(gòu),它的特點(diǎn)如下:
- 整個代碼段呈現(xiàn)為“鋸齒狀”,有深度縮進(jìn);
- “成功”邏輯被嵌入到
if
的布爾表達(dá)式為true
的代碼分支中;
很明顯,偽代碼段 1 的邏輯更容易理解,也更簡潔。Go 社區(qū)把這種 if 語句的使用方式稱為 if 語句的“快樂路徑(Happy Path)”原則,所謂“快樂路徑”也就是成功邏輯的代碼執(zhí)行路徑,它的特點(diǎn)是這樣的:
- 僅使用單分支控制結(jié)構(gòu);
- 當(dāng)布爾表達(dá)式求值為
false
時,也就是出現(xiàn)錯誤時,在單分支中快速返回; - 正常邏輯在代碼布局上始終“靠左”,這樣讀者可以從上到下一眼看到該函數(shù)正常邏輯的全貌;
- 函數(shù)執(zhí)行到最后一行代表一種成功狀態(tài)。
Go 社區(qū)推薦 Gopher 們在使用 if 語句時盡量符合這些原則,如果你的函數(shù)實(shí)現(xiàn)代碼不符合“快樂路徑”原則,你可以按下面步驟進(jìn)行重構(gòu):
- 嘗試將“正常邏輯”提取出來,放到“快樂路徑”中;
- 如果無法做到上一點(diǎn),很可能是函數(shù)內(nèi)的邏輯過
以上就是Go流程控制與快樂路徑原則的詳細(xì)內(nèi)容,更多關(guān)于Go流程控制路徑原則的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
使用golang實(shí)現(xiàn)在屏幕上打印進(jìn)度條的操作
這篇文章主要介紹了使用golang實(shí)現(xiàn)在屏幕上打印進(jìn)度條的操作,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-03-03Golang新提案:panic?能不能加個?PanicError?
這篇文章主要為大家介紹了Golang的新提案關(guān)于panic能不能加個PanicError的問題分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12go語言for循環(huán)中嵌套defer的執(zhí)行順序
在Go語言中,defer語句用于延遲函數(shù)調(diào)用的執(zhí)行,本文主要介紹了go語言for循環(huán)中嵌套defer的執(zhí)行順序,具有一定的參考價值,感興趣的可以了解一下2025-03-03Golang 標(biāo)準(zhǔn)庫 tips之waitgroup詳解
本篇文章給大家介紹Golang 標(biāo)準(zhǔn)庫 tips之waitgroup的相關(guān)知識,包括使用 channel 實(shí)現(xiàn) WaitGroup 的功能介紹,感興趣的朋友跟隨小編一起看看吧2021-07-07使用go語言實(shí)現(xiàn)Redis持久化的示例代碼
redis 是一個內(nèi)存數(shù)據(jù)庫,如果你把進(jìn)程殺掉,那么里面存儲的數(shù)據(jù)都會消失,那么這篇文章就是來解決 redis 持久化的問題,本文給大家介紹了使用go語言實(shí)現(xiàn)Redis持久化,需要的朋友可以參考下2024-07-07基于go中fyne gui的通達(dá)信數(shù)據(jù)導(dǎo)出工具詳解
這篇文章主要介紹了基于go中fyne gui的通達(dá)信數(shù)據(jù)導(dǎo)出工具,這是一個用 Go 語言開發(fā)的通達(dá)信數(shù)據(jù)導(dǎo)出工具,可以將通達(dá)信的本地數(shù)據(jù)導(dǎo)出為多種格式,方便用戶進(jìn)行數(shù)據(jù)分析和處理,需要的朋友可以參考下2024-12-12