golang中為什么不存在三元運算符詳解
三元運算符廣泛存在于其他語言中,比如:
python:
val = trueValue if expr else falseValue
javascript:
const val = expr ? trueValue : falseValue
c、c++:
const char *val = expr ? "trueValue" : "falseValue";
然而,被廣泛支持的三目運算符在golang中卻是不存在的!如果我們寫出類似下面的代碼:
val := expr ? "trueValue" : "falseValue"
那么編譯器就該抱怨了:invalid character U+003F '?'。意思是golang中不存在?這個運算符,編譯器不認(rèn)識而且非字母數(shù)字下劃線也不能用做變量名,自然也就當(dāng)作是非法字符了。
然而這是為什么呢,其實官方給出了解釋,這里簡單引用一下:
The reason ?: is absent from Go is that the language's designers had seen the operation used too often to create impenetrably complex expressions. The if-else form, although longer, is unquestionably clearer. A language needs only one conditional control flow construct.
golang中不存在?:運算符的原因是因為語言設(shè)計者已經(jīng)預(yù)見到三元運算符經(jīng)常被用來構(gòu)建一些極其復(fù)雜的表達(dá)式。雖然使用if進(jìn)行替代會讓代碼顯得更長,但這毫無疑問可讀性更強(qiáng)。一個語言只需要有一種條件判斷結(jié)構(gòu)就足夠了。
毫無疑問,這是在golang“大道至簡”的指導(dǎo)思想下的產(chǎn)物。
這段話其實沒問題,因為某些三元運算符的使用場景確實會降低代碼的可讀性:
const status = (type===1?(agagin===1?'再售':'已售'):'未售') const word = (res.distance === 0) ? 'a' : (res.distance === 1 && res.difference > 3) ? 'b' : (res.distance === 2 && res.difference > 5 && String(res.key).length > 5) ? 'c' : 'd';
乍一看確實很復(fù)雜,至少第二個表達(dá)式不花個20秒細(xì)看可能沒法理清控制流程(想象一下當(dāng)縮進(jìn)錯位或是完全沒有縮進(jìn)的時候)。
如果把它們直接轉(zhuǎn)化成if語句是這樣的:
let status = '' if (type === 1) { if (again === 1) { status = '再售' } else { status = '已售' } } else { status = '未售' } let word = '' if (res.distance === 0) { word = 'a' } else { if (res.distance === 1 && res.difference > 3) { word = 'b' } else { if (res.distance === 2 && res.difference > 5 && String(res.key).length > 5) { word = 'c' } else { word = 'd' } } }
看起來并沒有多少的改善,特別是例2,三層嵌套,不管是誰review到這段代碼難免不會抱怨你幾句。
然而事實上這些代碼是可以簡化的,考慮到三元運算符總是會給變量一個值,因此最后的else其實可以看作是變量的默認(rèn)值,于是代碼可以這么寫:
let status = '未售' if (type === 1) { if (again === 1) { status = '再售' } else { status = '已售' } } let word = 'd' if (res.distance === 0) { word = 'a' } else { if (res.distance === 1 && res.difference > 3) { word = 'b' } else { if (res.distance === 2 && res.difference > 5 && String(res.key).length > 5) { word = 'c' } } }
其次,對于例2,顯然可以使用else if來清除嵌套結(jié)構(gòu):
let word = 'd' if (res.distance === 0) { word = 'a' } else if (res.distance === 1 && res.difference > 3) { word = 'b' } else if (res.distance === 2 && res.difference > 5 && String(res.key).length > 5) { word = 'c' }
現(xiàn)在再來看,顯然使用if語句的版本的可讀性更高,邏輯也更清晰(通過去除嵌套)。
然而事實也不盡然。除了用三元運算符表達(dá)流程控制之外,事實上更常見更廣泛的一個應(yīng)用是如下這樣的表達(dá)式:
const val = expr ? trueValue : falseValue const func = (age) => age > 18 ? '成年人' : '未成年人'
類似上述通過一個簡短的條件表達(dá)式來確定變量的值,在開發(fā)中的出現(xiàn)頻率是相當(dāng)高的。這時三元運算符的意圖更清晰,可讀性也較if語句更高,特別是配合匿名函數(shù)(lambda表達(dá)式)使用可以極大簡化我們的代碼。
對此python的解決之道是之支持上述的簡化版三元表達(dá)式,同時表達(dá)式不支持嵌套,達(dá)到了揚長避短的目的。不過代價是編譯器的相關(guān)實現(xiàn)會復(fù)雜化。
而對于golang來說一個簡單的能只通過單遍掃描即可完成ast構(gòu)建的編譯器是其保持急速的構(gòu)建速度的秘訣之一,為了這樣簡單的功能增加編譯器實現(xiàn)的復(fù)雜度是不可接受的。同時由于golang“大道至簡”的哲學(xué),能用現(xiàn)有語法結(jié)構(gòu)解決的問題,自然不會再添加新的語法。
不過還是有辦法的,雖然不推薦:
func If(cond bool, a, b interface{}) { if cond { return a } return b } age := 20 val := If(age > 18, "成年人", "未成年人").(string)
不推薦這么做是有幾個原因:
- 使用接口導(dǎo)致性能下降
- 需要強(qiáng)制的類型斷言
- 不管三元表達(dá)式還是if語句,對于不會到達(dá)的分支是不會計算的,也就是惰性計算;而給函數(shù)傳遞參數(shù)時每一個表達(dá)式都會被計算
最后總結(jié)一下:
三元運算符的優(yōu)點:
- 對于簡短的表達(dá)式使用三元運算符表意更清晰,特別是在習(xí)慣了線性閱讀三元運算符表達(dá)式之后
- 不需要中間狀態(tài)(例如第一個例子中的let變量可以替換為const,代碼更健壯),心智負(fù)擔(dān)更低
- 沒有中間狀態(tài)也就意味著更少或完全沒有副作用,代碼更易跟蹤和維護(hù)
但三元運算符也有明顯的缺點:
- 對于復(fù)雜邏輯代碼可讀性較差(例如第一個例子中的status,需要在trueValue的位置進(jìn)行進(jìn)一步的條件判斷時)
- 容易被濫用,很多人將其用于替代if語句或是簡化復(fù)雜的if嵌套,這會導(dǎo)致上一條中所描述的結(jié)果
- 條件分支只能為表達(dá)式,不支持多條語句
所以這是一個見仁見智的問題,總之只能入鄉(xiāng)隨俗了。
參考
https://juejin.im/post/6844903561759850510
https://www.it-swarm.dev/zh/javascript/替代js中的嵌套三元運算符/1055944752/
https://golang.org/doc/faq#Does_Go_have_a_ternary_form
總結(jié)
到此這篇關(guān)于golang中為什么不存在三元運算符的文章就介紹到這了,更多相關(guān)golang不存在三元運算符內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
go語言中sort包的實現(xiàn)方法與應(yīng)用詳解
golang中也實現(xiàn)了排序算法的包sort包,所以下面這篇文章主要給大家介紹了關(guān)于go語言中sort包的實現(xiàn)方法與應(yīng)用的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友們可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2017-11-11Golang 實現(xiàn) Redis系列(六)如何實現(xiàn) pipeline 模式的 redis 客戶端
pipeline 模式的 redis 客戶端需要有兩個后臺協(xié)程負(fù)責(zé) tcp 通信,調(diào)用方通過 channel 向后臺協(xié)程發(fā)送指令,并阻塞等待直到收到響應(yīng),本文是使用 golang 實現(xiàn) redis 系列的第六篇, 將介紹如何實現(xiàn)一個 Pipeline 模式的 Redis 客戶端。2021-07-07GoFrame通用類型變量gvar與interface基本使用對比
這篇文章主要為大家介紹了GoFrame通用類型變量gvar與interface基本使用對比,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06Go語言并發(fā)之Sync包的6個關(guān)鍵概念總結(jié)
這篇文章主要為大家詳細(xì)介紹了Go語言并發(fā)中Sync包的6個關(guān)鍵概念,文中的示例代碼講解詳細(xì),對我們深入學(xué)習(xí)Go語言有一定的幫助,需要的可以參考一下2023-05-05