最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

探索Go語(yǔ)言中的switch高級(jí)用法

 更新時(shí)間:2024年10月01日 16:37:26   作者:apocelipes  
在Go語(yǔ)言中,switch語(yǔ)句除了常見(jiàn)的用法外,還有一種不常用但有趣的寫(xiě)法,這種寫(xiě)法中,switch后面不跟任何表達(dá)式,而每個(gè)case后面跟的是返回bool類(lèi)型的函數(shù)調(diào)用表達(dá)式,這實(shí)際上是一個(gè)等價(jià)于switch true的用法,通過(guò)從上到下逐一比較case后的表達(dá)式是否為true來(lái)決定執(zhí)行哪個(gè)分支

最近翻開(kāi)源代碼的時(shí)候看到了一種很有意思的switch用法,分享一下。

注意這里討論的不是typed switch,也就是case語(yǔ)句后面是類(lèi)型的那種。

直接看代碼:

	func (s *systemd) Status() (Status, error) {

	exitCode, out, err := s.runWithOutput("systemctl", "is-active", s.unitName())

	if exitCode == 0 && err != nil {

	return StatusUnknown, err

	}

	


	switch {

	case strings.HasPrefix(out, "active"):

	return StatusRunning, nil

	case strings.HasPrefix(out, "inactive"):

	// inactive can also mean its not installed, check unit files

	exitCode, out, err := s.runWithOutput("systemctl", "list-unit-files", "-t", "service", s.unitName())

	if exitCode == 0 && err != nil {

	return StatusUnknown, err

	}

	if strings.Contains(out, s.Name) {

	// unit file exists, installed but not running

	return StatusStopped, nil

	}

	// no unit file

	return StatusUnknown, ErrNotInstalled

	case strings.HasPrefix(out, "activating"):

	return StatusRunning, nil

	case strings.HasPrefix(out, "failed"):

	return StatusUnknown, errors.New("service in failed state")

	default:

	return StatusUnknown, ErrNotInstalled

	}

	}

你也可以在這找到它:代碼鏈接

簡(jiǎn)單解釋下這段代碼在做什么:調(diào)用systemctl命令檢查指定的服務(wù)的運(yùn)行狀態(tài),具體做法是過(guò)濾systemctl的輸出然后根據(jù)得到的字符串的前綴判斷當(dāng)前的運(yùn)行狀態(tài)。

有意思的在于這個(gè)switch,首先它后面沒(méi)有任何表達(dá)式;其次在每個(gè)case后面都是個(gè)函數(shù)調(diào)用表達(dá)式,返回值都是bool類(lèi)型的。

雖然看起來(lái)很怪異,但這段代碼肯定沒(méi)有語(yǔ)法問(wèn)題,可以編譯通過(guò);也沒(méi)有語(yǔ)義或者邏輯問(wèn)題,因?yàn)槿思矣玫暮煤玫模@個(gè)項(xiàng)目接近4000個(gè)星星不是大家亂點(diǎn)的。

這里就不賣(mài)關(guān)子了,直接公布答案:

  • 如果switch后面沒(méi)有任何表達(dá)式,那么它等價(jià)于這個(gè):switch true
  • case表達(dá)式按從上到下從左到右的順序求值;
  • 如果case后面的表達(dá)式求出來(lái)的值和switch后面的表達(dá)式的值一樣,那么就進(jìn)入這個(gè)分支,其他case被忽略(除非用了fallthrough,但這會(huì)直接跳進(jìn)下一個(gè)case的分支,不會(huì)執(zhí)行下一個(gè)case上的表達(dá)式)。

那么上面那一串代碼就好理解了:

  • 首先是switch true,期待有個(gè)case能求出true這個(gè)值;
  • 從上到下執(zhí)行strings.HasPrefix,如果是false就往下到下一個(gè)case,如果是true就進(jìn)入這個(gè)case的分支。

它等價(jià)于下面這段:

	func (s *systemd) Status() (Status, error) {

	exitCode, out, err := s.runWithOutput("systemctl", "is-active", s.unitName())

	if exitCode == 0 && err != nil {

	return StatusUnknown, err

	}

	


	if strings.HasPrefix(out, "active") {

	return StatusRunning, nil

	}

	if strings.HasPrefix(out, "inactive") {

	// inactive can also mean its not installed, check unit files

	exitCode, out, err := s.runWithOutput("systemctl", "list-unit-files", "-t", "service", s.unitName())

	if exitCode == 0 && err != nil {

	return StatusUnknown, err

	}

	if strings.Contains(out, s.Name) {

	// unit file exists, installed but not running

	return StatusStopped, nil

	}

	// no unit file

	return StatusUnknown, ErrNotInstalled

	}

	if strings.HasPrefix(out, "activating") {

	return StatusRunning, nil

	}

	if strings.HasPrefix(out, "failed") {

	return StatusUnknown, errors.New("service in failed state")

	}

	


	return StatusUnknown, ErrNotInstalled

	}

可以看到,光從可讀性上來(lái)說(shuō)的話(huà)兩者很難說(shuō)誰(shuí)更優(yōu)秀;兩者同樣需要注意把常見(jiàn)的情況放在最前面來(lái)減少不必要的匹配(這里的switch-case不能像給整數(shù)常量時(shí)那樣直接進(jìn)行跳轉(zhuǎn),實(shí)際執(zhí)行和上面給出的if語(yǔ)句是差不多的)。

那么我們?cè)賮?lái)看看兩者的生成代碼,通常我不喜歡去研究編譯器生成的代碼,但這次是個(gè)小例外,對(duì)于執(zhí)行流程上很接近的兩段代碼,編譯器會(huì)怎么處理呢?

我們做個(gè)簡(jiǎn)化版的例子:

func status1(cmdOutput string, flag int) int {
	switch {
	case strings.HasPrefix(cmdOutput, "active"):
	return 1
	case strings.HasPrefix(cmdOutput, "inactive"):
	if flag > 0 {
	return 2
	}
	return -1
	case strings.HasPrefix(cmdOutput, "activating"):
	return 1
	case strings.HasPrefix(cmdOutput, "failed"):
	return -1
	default:
	return -2
	}
	}
	func status2(cmdOutput string, flag int) int {
	if strings.HasPrefix(cmdOutput, "active") {
	return 1
	}
	if strings.HasPrefix(cmdOutput, "inactive") {
	if flag > 0 {
	return 2
	}
	return -1
	}
	if strings.HasPrefix(cmdOutput, "activating") {
	return 1
	}
	if strings.HasPrefix(cmdOutput, "failed") {
	return -1
	}
	return -2
	}

這是switch版本的匯編:

	main_status1_pc0:

	TEXT main.status1(SB), ABIInternal, $40-24

	CMPQ SP, 16(R14)

	PCDATA $0, $-2

	JLS main_status1_pc273

	PCDATA $0, $-1

	SUBQ $40, SP

	MOVQ BP, 32(SP)

	LEAQ 32(SP), BP

	FUNCDATA $0, gclocals·wgcWObbY2HYnK2SU/U22lA==(SB)

	FUNCDATA $1, gclocals·J5F+7Qw7O7ve2QcWC7DpeQ==(SB)

	FUNCDATA $5, main.status1.arginfo1(SB)

	FUNCDATA $6, main.status1.argliveinfo(SB)

	PCDATA $3, $1

	MOVQ CX, main.flag+64(SP)

	MOVQ AX, main.cmdOutput+48(SP)

	MOVQ BX, main.cmdOutput+56(SP)

	PCDATA $3, $-1

	MOVL $6, DI

	LEAQ go:string."active"(SB), CX

	PCDATA $1, $0

	CALL strings.HasPrefix(SB)

	NOP

	TESTB AL, AL

	JNE main_status1_pc258

	MOVQ main.cmdOutput+48(SP), AX

	MOVQ main.cmdOutput+56(SP), BX

	LEAQ go:string."inactive"(SB), CX

	MOVL $8, DI

	NOP

	CALL strings.HasPrefix(SB)

	TESTB AL, AL

	JEQ main_status1_pc147

	MOVQ main.flag+64(SP), CX

	TESTQ CX, CX

	JLE main_status1_pc130

	MOVL $2, AX

	MOVQ 32(SP), BP

	ADDQ $40, SP

	RET

	main_status1_pc130:

	MOVQ $-1, AX

	MOVQ 32(SP), BP

	ADDQ $40, SP

	RET

	main_status1_pc147:

	MOVQ main.cmdOutput+48(SP), AX

	MOVQ main.cmdOutput+56(SP), BX

	LEAQ go:string."activating"(SB), CX

	MOVL $10, DI

	CALL strings.HasPrefix(SB)

	TESTB AL, AL

	JNE main_status1_pc243

	MOVQ main.cmdOutput+48(SP), AX

	MOVQ main.cmdOutput+56(SP), BX

	LEAQ go:string."failed"(SB), CX

	MOVL $6, DI

	PCDATA $1, $1

	CALL strings.HasPrefix(SB)

	TESTB AL, AL

	JEQ main_status1_pc226

	MOVQ $-1, AX

	MOVQ 32(SP), BP

	ADDQ $40, SP

	RET

	main_status1_pc226:

	MOVQ $-2, AX

	MOVQ 32(SP), BP

	ADDQ $40, SP

	RET

	main_status1_pc243:

	MOVL $1, AX

	MOVQ 32(SP), BP

	ADDQ $40, SP

	RET

	main_status1_pc258:

	MOVL $1, AX

	MOVQ 32(SP), BP

	ADDQ $40, SP

	RET

	main_status1_pc273:

	NOP

	PCDATA $1, $-1

	PCDATA $0, $-2

	MOVQ AX, 8(SP)

	MOVQ BX, 16(SP)

	MOVQ CX, 24(SP)

	CALL runtime.morestack_noctxt(SB)

	MOVQ 8(SP), AX

	MOVQ 16(SP), BX

	MOVQ 24(SP), CX

	PCDATA $0, $-1

	JMP main_status1_pc0

我把inline給關(guān)了,不然hasprefix內(nèi)聯(lián)出來(lái)的東西會(huì)導(dǎo)致整個(gè)匯編代碼難以閱讀。

上面的代碼還是很好理解的,“active”和“inactive”的case被放在一起,如果匹配到了就跳轉(zhuǎn)進(jìn)入對(duì)應(yīng)的分支;“activing”和“failed”的case也放在了一起,匹配到之后的操作與前面兩個(gè)case一樣(實(shí)際上上面兩個(gè)case的匹配執(zhí)行完就會(huì)跳轉(zhuǎn)到這兩個(gè),至于為啥要多一次跳轉(zhuǎn)我沒(méi)深究,可能是為了提高L1d的命中率,一大塊指令可能會(huì)導(dǎo)致緩存里放不下從而付出更新緩存的代價(jià),而有流水線優(yōu)化的情況下一個(gè)jmp帶來(lái)的開(kāi)銷(xiāo)可能低于緩存未命中的懲罰,不過(guò)這在實(shí)踐里很難測(cè)量,權(quán)當(dāng)我在自言自語(yǔ)也行)。最后那一串帶ret的語(yǔ)句塊就是對(duì)應(yīng)的case的分支。

再來(lái)看看if的代碼:

	main_status2_pc0:

	TEXT main.status2(SB), ABIInternal, $40-24

	CMPQ SP, 16(R14)

	PCDATA $0, $-2

	JLS main_status2_pc273

	PCDATA $0, $-1

	SUBQ $40, SP

	MOVQ BP, 32(SP)

	LEAQ 32(SP), BP

	FUNCDATA $0, gclocals·wgcWObbY2HYnK2SU/U22lA==(SB)

	FUNCDATA $1, gclocals·J5F+7Qw7O7ve2QcWC7DpeQ==(SB)

	FUNCDATA $5, main.status2.arginfo1(SB)

	FUNCDATA $6, main.status2.argliveinfo(SB)

	PCDATA $3, $1

	MOVQ CX, main.flag+64(SP)

	MOVQ AX, main.cmdOutput+48(SP)

	MOVQ BX, main.cmdOutput+56(SP)

	PCDATA $3, $-1

	MOVL $6, DI

	LEAQ go:string."active"(SB), CX

	PCDATA $1, $0

	CALL strings.HasPrefix(SB)

	NOP

	TESTB AL, AL

	JNE main_status2_pc258

	MOVQ main.cmdOutput+48(SP), AX

	MOVQ main.cmdOutput+56(SP), BX

	LEAQ go:string."inactive"(SB), CX

	MOVL $8, DI

	NOP

	CALL strings.HasPrefix(SB)

	TESTB AL, AL

	JEQ main_status2_pc147

	MOVQ main.flag+64(SP), CX

	TESTQ CX, CX

	JLE main_status2_pc130

	MOVL $2, AX

	MOVQ 32(SP), BP

	ADDQ $40, SP

	RET

	main_status2_pc130:

	MOVQ $-1, AX

	MOVQ 32(SP), BP

	ADDQ $40, SP

	RET

	main_status2_pc147:

	MOVQ main.cmdOutput+48(SP), AX

	MOVQ main.cmdOutput+56(SP), BX

	LEAQ go:string."activating"(SB), CX

	MOVL $10, DI

	CALL strings.HasPrefix(SB)

	TESTB AL, AL

	JNE main_status2_pc243

	MOVQ main.cmdOutput+48(SP), AX

	MOVQ main.cmdOutput+56(SP), BX

	LEAQ go:string."failed"(SB), CX

	MOVL $6, DI

	PCDATA $1, $1

	CALL strings.HasPrefix(SB)

	TESTB AL, AL

	JEQ main_status2_pc226

	MOVQ $-1, AX

	MOVQ 32(SP), BP

	ADDQ $40, SP

	RET

	main_status2_pc226:

	MOVQ $-2, AX

	MOVQ 32(SP), BP

	ADDQ $40, SP

	RET

	main_status2_pc243:

	MOVL $1, AX

	MOVQ 32(SP), BP

	ADDQ $40, SP

	RET

	main_status2_pc258:

	MOVL $1, AX

	MOVQ 32(SP), BP

	ADDQ $40, SP

	RET

	main_status2_pc273:

	NOP

	PCDATA $1, $-1

	PCDATA $0, $-2

	MOVQ AX, 8(SP)

	MOVQ BX, 16(SP)

	MOVQ CX, 24(SP)

	CALL runtime.morestack_noctxt(SB)

	MOVQ 8(SP), AX

	MOVQ 16(SP), BX

	MOVQ 24(SP), CX

	PCDATA $0, $-1

	JMP main_status2_pc0

除了函數(shù)名子不一樣之外,其他是一模一樣的,可以說(shuō)兩者在生成代碼上也沒(méi)有區(qū)別。

你可以在這里看到代碼和他們的編譯產(chǎn)物:Compiler Explorer

既然生成代碼是一樣的,那性能就沒(méi)必要測(cè)量了,因?yàn)榭隙ㄊ且粯拥摹?/p>

最后總結(jié)一下這種不常用的switch寫(xiě)法,形式如下:

	switch {

	case 表達(dá)式1: // 如果是true

	do works1

	case 表達(dá)式2: // 如果是true

	do works2

	default:

	都不是true就會(huì)到這里

	}

考慮到在性能上這并沒(méi)有什么優(yōu)勢(shì),而且對(duì)于初次見(jiàn)到這個(gè)寫(xiě)法的人可能不能很快理解它的含義,所以這個(gè)寫(xiě)法的使用場(chǎng)景我目前能想到的只有一處:

如果你的數(shù)據(jù)有固定的2種以上的前綴/后綴/某種模式,因?yàn)闆](méi)法用固定的常量去表示這種情況,那么用case加上一個(gè)簡(jiǎn)單的表達(dá)式(函數(shù)調(diào)用之類(lèi)的)會(huì)比用if更緊湊,也能更好地表達(dá)語(yǔ)義,case越多效果越明顯。比如我在開(kāi)頭舉的那個(gè)例子。

如果你的代碼不符合上述情況,那還是老老實(shí)實(shí)用if會(huì)更好。

話(huà)說(shuō)回來(lái),雖然你機(jī)會(huì)沒(méi)啥機(jī)會(huì)寫(xiě)出這種switch語(yǔ)句,但最好還是得看懂,不然下回看見(jiàn)它就只能干瞪眼了。

參考

https://go.dev/ref/spec#Switch_statements

到此這篇關(guān)于探索Go語(yǔ)言中的switch高級(jí)用法的文章就介紹到這了,更多相關(guān)go中不常見(jiàn)的switch用法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Golang?Mutex互斥鎖源碼分析

    Golang?Mutex互斥鎖源碼分析

    本篇文章,我們將一起來(lái)探究下Golang?Mutex底層是如何實(shí)現(xiàn)的,知其然,更要知其所以然。文中的示例代碼講解詳細(xì),感興趣的可以了解一下
    2022-10-10
  • Golang的循環(huán)中break和continue語(yǔ)句的用法講解

    Golang的循環(huán)中break和continue語(yǔ)句的用法講解

    這篇文章主要介紹了Golang的循環(huán)中break和continue語(yǔ)句的用法講解,是Go語(yǔ)言入門(mén)學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下
    2015-10-10
  • 詳解Golang中創(chuàng)建error的方式總結(jié)與應(yīng)用場(chǎng)景

    詳解Golang中創(chuàng)建error的方式總結(jié)與應(yīng)用場(chǎng)景

    Golang中創(chuàng)建error的方式包括errors.New、fmt.Errorf、自定義實(shí)現(xiàn)了error接口的類(lèi)型等,本文主要為大家介紹了這些方式的具體應(yīng)用場(chǎng)景,需要的可以參考一下
    2023-07-07
  • Go語(yǔ)言面向?qū)ο筇匦?封裝、組合、多態(tài))超詳細(xì)講解

    Go語(yǔ)言面向?qū)ο筇匦?封裝、組合、多態(tài))超詳細(xì)講解

    面向?qū)ο缶幊叹哂懈邇?nèi)聚低耦合,易維護(hù)、易擴(kuò)展、易復(fù)用的優(yōu)點(diǎn),而Go語(yǔ)言中也是可以進(jìn)行面向?qū)ο缶幊痰?這篇文章主要介紹了Go語(yǔ)言面向?qū)ο筇匦?封裝、組合、多態(tài))的相關(guān)資料,需要的朋友可以參考下
    2026-03-03
  • golang生成JSON以及解析JSON

    golang生成JSON以及解析JSON

    這篇文章主要介紹了golang生成JSON以及解析JSON,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • go語(yǔ)言go?func(){select{}}()的用法

    go語(yǔ)言go?func(){select{}}()的用法

    本文主要介紹了go語(yǔ)言go?func(){select{}}()的用法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-02-02
  • 詳解Go 并發(fā)

    詳解Go 并發(fā)

    這篇文章主要介紹了Go 并發(fā)的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)go語(yǔ)言,感興趣的朋友可以了解下
    2020-09-09
  • Golang有類(lèi)型常量和無(wú)類(lèi)型常量的區(qū)別

    Golang有類(lèi)型常量和無(wú)類(lèi)型常量的區(qū)別

    本文主要介紹了Golang有類(lèi)型常量和無(wú)類(lèi)型常量的區(qū)別,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • golang interface判斷為空nil的實(shí)現(xiàn)代碼

    golang interface判斷為空nil的實(shí)現(xiàn)代碼

    這篇文章主要介紹了golang interface判斷為空nil的實(shí)現(xiàn)代碼,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-04-04
  • golang中sync.Mutex的實(shí)現(xiàn)方法

    golang中sync.Mutex的實(shí)現(xiàn)方法

    本文主要介紹了golang中sync.Mutex的實(shí)現(xiàn)方法,mutex?主要有兩個(gè)?method:?Lock()?和?Unlock(),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-04-04

最新評(píng)論

梅州市| 宜宾县| 浦北县| 谢通门县| 龙山县| 济南市| 句容市| 沙田区| 达拉特旗| 株洲县| 威海市| 囊谦县| 大埔县| 长沙县| 循化| 仙桃市| 阳信县| 靖西县| 龙南县| 卢龙县| 崇仁县| 轮台县| 蕲春县| 丹凤县| 荥阳市| 肥东县| 湛江市| 商南县| 和平县| 固镇县| 香河县| 兴仁县| 仁化县| 家居| 青阳县| 山东省| 濮阳市| 商洛市| 贵州省| 阳城县| 丘北县|