第 11 講:運(yùn)算符(三):比較、邏輯和條件運(yùn)算符
下面我們介紹一下比較運(yùn)算符、邏輯運(yùn)算符和條件運(yùn)算符。這三種運(yùn)算符需要一起講,因?yàn)樗鼈冎g是存在關(guān)聯(lián)和關(guān)系的。
Part 1 六種比較運(yùn)算符
先別被標(biāo)題嚇?biāo)懒恕AN比較運(yùn)算符實(shí)際上就是我們經(jīng)常在數(shù)學(xué)上使用的大于、大于等于、小于、小于等于、等于和不等于這六種比較符號(hào)。在 C# 里,和 C 語(yǔ)言一致,分別寫成 >
、>=
、<
、<=
、==
和 !=
。特別需要說(shuō)明的是,等號(hào)需要用兩個(gè)等號(hào)表達(dá),因?yàn)閱蝹€(gè)等號(hào)已經(jīng)用來(lái)表達(dá)賦值過(guò)程了。為了避免賦值過(guò)程和比較過(guò)程使用同一個(gè)符號(hào)產(chǎn)生語(yǔ)義沖突,比較相等就多用了一次等號(hào)符號(hào)。
實(shí)際上也沒什么好說(shuō)的。它實(shí)際上就是在比較數(shù)據(jù)的大小。將比較結(jié)果(條件是否正確)以一個(gè) bool
Part 2 八種邏輯運(yùn)算符
C# 里有四種邏輯運(yùn)算,分別是且(&&
)、或(||
)、貪婪且(&
)、貪婪或(|
)、且元(false
)、或元(true
)、異或(^
)和邏輯取反(!
)。我相信你肯定只知道邏輯且、或、非(邏輯取反),最多多一個(gè)邏輯異或運(yùn)算。其它四個(gè)估計(jì)你完全不知道。這也沒關(guān)系,我們慢慢來(lái)梳理它們。
2-1 邏輯且、或運(yùn)算
我們使用 &&
連接兩個(gè) bool
類型的變量,用來(lái)表達(dá)和表示兩個(gè)條件必須都成立的時(shí)候,才成立。
這個(gè) condition
的值是 true
還是 false
呢?顯然是 true
,因?yàn)閮蓚€(gè)條件 a
和 b
全都是正確的表達(dá)式,因此相當(dāng)于在算 true && true
,那當(dāng)然結(jié)果就是 true
了。
||
和 &&
顯然,兩個(gè)都是 true
的結(jié)果。只有兩個(gè)都是 false
的時(shí)候,結(jié)果才是 false
。因此這個(gè)例子的 condition
結(jié)果依舊是 true
2-2 貪婪邏輯運(yùn)算以及短路現(xiàn)象
要說(shuō)清楚貪婪邏輯運(yùn)算,必須要先說(shuō)一下邏輯且和邏輯或本身存在的一種短路現(xiàn)象,它分邏輯且和邏輯或兩種運(yùn)算來(lái)解釋:
邏輯且運(yùn)算(
&&
):由于兩個(gè)條件里,有一個(gè)為false
的時(shí)候,就已經(jīng)可以確定表達(dá)式結(jié)果,因此如果當(dāng)左側(cè)結(jié)果就是false
的時(shí)候,右側(cè)結(jié)果不論是普通表達(dá)式,還是帶有運(yùn)算過(guò)程的表達(dá)式,都不會(huì)被得到執(zhí)行;邏輯或運(yùn)算(
||
):由于兩個(gè)條件里,有一個(gè)為true
的時(shí)候,就已經(jīng)可以確定表達(dá)式結(jié)果,因此如果當(dāng)左側(cè)結(jié)果就是true
的時(shí)候,右側(cè)結(jié)果不論是普通表達(dá)式,還是帶有運(yùn)算過(guò)程的表達(dá)式,都不會(huì)被得到執(zhí)行。
可以從內(nèi)容里看出,它們的差距就在 true
和 false
這個(gè)詞不一樣。下面我們來(lái)說(shuō)一下這個(gè)到底是什么意思。
可以看到,其實(shí)兩個(gè)例子的結(jié)果分別是 0, 4, true
和 0, 3, true
。非貪婪運(yùn)算在計(jì)算 --a <= 0
的時(shí)候,發(fā)現(xiàn) --
是前綴運(yùn)算,因此先把 a
--a
的結(jié)果就是此時(shí) a
的數(shù)值(即 0);最后,--a <= 0
就一定是成立的,因此整個(gè)表達(dá)式的結(jié)果是 true
。
因?yàn)槭褂玫氖沁壿嫽蜻\(yùn)算,因此左側(cè)條件已經(jīng)可以確定整個(gè)表達(dá)式 --a <= 0 || --b <= 0
的結(jié)果了,那么,--b <= 0
就不必計(jì)算了,即使 --b
會(huì)影響 b
的結(jié)果,此時(shí)的 b
照樣不會(huì)減去 1 個(gè)單位,因此 a
變成 0,b
沒有任何改動(dòng),且最終的結(jié)果是 true
。
貪婪邏輯或運(yùn)算說(shuō)白了就是沒有短路現(xiàn)象的邏輯或運(yùn)算。不管左邊是不是能確定表達(dá)式結(jié)果,右側(cè)的表達(dá)式依舊需要運(yùn)算。那么,結(jié)果自然就是 0, 3, true
了(雖然我們知道,就算 b
變成 3,--b <= 0
也是 false
的;但是因?yàn)?true
或一個(gè)不管什么東西的結(jié)果都是 true
)。
同樣地,邏輯且和貪婪邏輯且運(yùn)算是一樣的道理。
思考一下,這個(gè)例子里的結(jié)果分別是多少,以及把貪婪邏輯且運(yùn)算改成邏輯且運(yùn)算后,結(jié)果又是多少。
2-3 邏輯異或運(yùn)算
異或運(yùn)算類似于我們邏輯上理解的“要么……要么……”。如果別人告訴你,要么蘋果要么香蕉的時(shí)候,如果你都選擇了,或者都不要的話,這兩種情況都是不成立的。因?yàn)橐幢硎纠锩娴臇|西你必須選,但是只能選一個(gè)。異或運(yùn)算就是這么一個(gè)邏輯。
由于 a > 10
的結(jié)果是 false
,而 b >= 3
的結(jié)果是 true
。按照邏輯異或運(yùn)算的規(guī)則,因?yàn)橛星覂H有一個(gè)表達(dá)式結(jié)果為 ,因此 condition
的結(jié)果就是 false ^ true = true
。
最后,我們還剩下邏輯元運(yùn)算兩個(gè)(
true
和false
)。這一點(diǎn)我們先放一邊,我們先講條件運(yùn)算。
2-4 邏輯取反運(yùn)算
邏輯取反運(yùn)算比起前面的就要簡(jiǎn)單很多了。邏輯取反就是把對(duì)改成錯(cuò)、把錯(cuò)改成對(duì)的過(guò)程。
如果 a > b
條件為真(即 true
的話),那么 condition
的結(jié)果就為 false
。
請(qǐng)注意,!
運(yùn)算符僅用來(lái)表達(dá)一個(gè) bool
結(jié)果的取反,因此必須要在表達(dá)式整體上打括號(hào)后,然后前面追加 !
。如果寫成 !a > b
的話,C# 會(huì)產(chǎn)生一個(gè)編譯器錯(cuò)誤,告訴你 !a
a
是一個(gè)數(shù)。
Part 3 條件運(yùn)算符
條件運(yùn)算符(Conditional Operator)是一個(gè)需要三個(gè)表達(dá)式才能操作的運(yùn)算符,它用 ?
和 :
作為分隔,問(wèn)號(hào)的前面寫條件(一個(gè) bool
類型的變量),然后 ?
和 :
中間寫這個(gè)條件成立的時(shí)候的數(shù)值,:
后寫條件不成立的時(shí)候的數(shù)值。兩個(gè)數(shù)值必須是同一種數(shù)據(jù)類型的。
舉個(gè)例子:
這表示,當(dāng) a > 10
c
的數(shù)值,否則 c
為 40。
Part 4 邏輯元運(yùn)算
坐穩(wěn)了。這一節(jié)的難度有點(diǎn)大,而且很考察各位的邏輯推理能力。千萬(wàn)不要掉以輕心。
邏輯元運(yùn)算(Meta Logical Operator)一共有兩個(gè)(true
和 false
)。不要以為 true
和 false
只能表示一個(gè)條件為真和假,它還有別的意思:當(dāng)成一個(gè)運(yùn)算符,記作 true(表達(dá)式)
和 false(表達(dá)式)
。
邏輯元運(yùn)算將 &&
和 &
,還有 ||
和 |
關(guān)聯(lián)起來(lái)。公式大概是這樣的:
a && b
等價(jià)于false(a) ? a : a & b
;a || b
等價(jià)于true(a) ? a : a | b
。
比如 a && b
。我們先運(yùn)算 false(a)
的結(jié)果,如果這個(gè)結(jié)果是 true
,我們就直接把 a
作為 a && b
的結(jié)果;否則,就取 a & b
整個(gè)表達(dá)式的結(jié)果。
不好理解?行,我們拿駕車的例子給大家介紹一下邏輯元運(yùn)算。假設(shè)我們開著一輛車向前行駛。遇到紅綠燈路口需要過(guò)馬路。那么,如果我們將車輛油箱里的油還剩下多少分成三種狀態(tài):
紅色:沒油了;
黃色:有一點(diǎn)油,但是要提出警告,因?yàn)榭赡苓^(guò)不了馬路;
綠色:有充足的油前進(jìn)。
我們把紅綠燈的狀態(tài)也表示成三種情況:
紅色:紅燈無(wú)法前進(jìn);
黃色:黃燈;
綠色:綠燈可以前進(jìn)。
那么,假設(shè)我們把“帶有紅色、黃色、綠色”這三種狀態(tài)的邏輯稱為“狀態(tài)邏輯”。那么,狀態(tài)邏輯的運(yùn)算應(yīng)該如何呢?
如果兩個(gè)條件里只要有一個(gè)是紅色的話,我們就斷定結(jié)果狀態(tài)一定是紅色的;
如果兩個(gè)條件里兩個(gè)都是黃色的話,我們也認(rèn)為最終狀態(tài)也是紅色的;
如果兩個(gè)條件有一個(gè)黃色的話,我們就認(rèn)為結(jié)果狀態(tài)是黃色的;
如果前面都不成立,則現(xiàn)在只剩下綠色了,因此我們認(rèn)為其它情況下的結(jié)果狀態(tài)都是綠色的。
那么,我們可以具象化邏輯:定義一個(gè)“狀態(tài)邏輯”的變量 canGo
,表示是否可以前行。它取決于“油箱是否有油”和“前面紅綠燈是否可以允許前進(jìn)”兩個(gè)因素,因此我們需要把這兩個(gè)條件用邏輯且連接起來(lái):
其中,
fuelStatus
是油箱有沒有油的狀態(tài)(有油,有油但是少、沒有油);signalStatus
是紅綠燈是否允許我們前進(jìn)的狀態(tài)(紅燈、黃燈和綠燈)。
true
和 false
這兩個(gè)邏輯元運(yùn)算的過(guò)程了。
true(狀態(tài))
:狀態(tài)是綠色的時(shí)候,結(jié)果為true
,否則為false
;false(狀態(tài))
:狀態(tài)是黃色或紅色的時(shí)候,結(jié)果為true
,否則為false
。
很好。我們最后帶入公式,看看是否邏輯正常:fuelStatus && signalStatus
用邏輯元運(yùn)算來(lái)表達(dá)的話,可以展開成 false(fuelStatus) ? fuelStatus : fuelStatus & signalStatus
。
這個(gè)邏輯就很好理解了。如果油箱狀態(tài)是紅色(或黃色)的時(shí)候,我們就可以直接認(rèn)為 canGo
直接是紅色(或黃色)了:因?yàn)榧热挥拖涠紱]油了,那我們自然就不可能前進(jìn)(這一點(diǎn)就是表達(dá)式里體現(xiàn)到的短路現(xiàn)象)。但是,如果油箱的油還多的話,我們這個(gè)時(shí)候就肯定要看紅綠燈是否允許我們前行,因此,我們還要看 signalStatus
的結(jié)果才行。
那么,為什么不是直接看 signalStatus
的狀態(tài)作為結(jié)果,而是 fuelStatus & signalStatus
呢?因?yàn)椋覀儾荒軆H通過(guò)信號(hào)燈的狀態(tài)就確定我們是否可以繼續(xù)通行,因此需要看的是油箱和信號(hào)燈兩個(gè)狀態(tài)才可以斷言結(jié)果。你換個(gè)角度想一想 bool
里 &&
和 &
,就可以想通這個(gè)道理了。
這就稱為邏輯元運(yùn)算。邏輯元運(yùn)算決定了條件是否成立的底層邏輯,而 &&
和 ||
可以認(rèn)為是用貪婪運(yùn)算和邏輯元運(yùn)算共同構(gòu)成的一個(gè)復(fù)雜表達(dá)式。
邏輯元運(yùn)算是唯一一個(gè)無(wú)法直接使用的運(yùn)算符;換句話說(shuō),你無(wú)法使用代碼書寫的方式來(lái)使用這個(gè)運(yùn)算符。比如說(shuō)你這么寫代碼:
,這個(gè)寫法就是不行的。它的出現(xiàn)實(shí)際上是為了輔助false(condition)
&
運(yùn)算符(貪婪邏輯且運(yùn)算)和|
運(yùn)算符(貪婪邏輯或運(yùn)算)提供幫助,構(gòu)成&&
和||
的中間運(yùn)算過(guò)程。正是因?yàn)樗侵虚g運(yùn)算過(guò)程,因此難度比套用公式還要難理解一些。
Part 5 總結(jié)
本節(jié)內(nèi)容難度可能有點(diǎn)大,考察各位對(duì)邏輯的理解。比較運(yùn)算其實(shí)沒有什么可以說(shuō)的,主要就是邏輯運(yùn)算比較不好理解,短路現(xiàn)象、元運(yùn)算等等。