邏輯運算子

極為常見

真正APCS考的條件判斷絕對不會這麼單純,通常在同一個 if 裡不會只有一個條件要判斷,比如說:

程式和數學都超電的人往往會被膜拜 Orz

老人、兒童都能折抵票價

沒有錢就買不了新手機 QQ

以上都蘊含「邏輯運算子」的精神,以下就讓我們一起來看看常見的 3 個邏輯運算子!

&& (且,and)

if (條件1 && 條件2) {
    (敘述1)
}

上面那個程式,要條件 1 和條件 2 都滿足才會執行敘述 1,也就是說,只要一個不成立,敘述 1 就不會被執行

事實上,條件式本身可以被看做一個布林值(所以型態為 bool 的變數其實也可以放進 if ),因此其實 () 裡裝的是一個布林運算的結果。

以最前面「程式和數學都超電的人往往會被膜拜 Orz」為例:

bool programming_orz, math_orz;
...
if (programming_orz && math_orz) {
    cout << "Orz Orz Orz";
}
真值表

在討論這種邏輯運算問題的時候,我們常會畫一個表把 truefalse 填進去,這就叫做真值表,舉例如下:

x1x2x1 && x2
111
010
100
000

||(或,or)

if (條件1 || 條件2){
    (敘述1)
}

&& 不同的是,上面那個程式,只要條件 1 和條件 2 滿足其中一個就會執行敘述 1,也就是說,要兩個都不成立,敘述 1 才不會被執行,雖然看起來很簡單,但是將來條件越來越複雜的時候,很常就是因為 &&|| 判斷寫爛了,所以要特別小心。

以「老人、兒童都能折抵票價」為例:

bool is_old, is_child;
...
if (is_old || is_child) {
    cout << "You can get cheaper ticket!";
}
真值表
x1x2x1 || x2
111
011
101
000

! (不,not)

if (!條件1) {
    (敘述1)
}

! 比較簡潔,只要條件1 false 就會執行敘述 1。

以「沒有錢就買不了新手機 QQ」為例:

bool have_money;
...
if (!have_money) {
    cout << "Bye new cellphone...";
}

這個性質可以被運用到前面的 &&||。有一個東西叫做「笛摩根定理」,大概是這樣的:

! ( a && b ) = ( !a ) || ( !b )

1

! ( a || b ) = ( !a ) && ( !b )

2

當遇到比較複雜的判斷式,可以善用笛摩根定理簡化判斷式,這樣讀code也會比較省力。

真值表
x1!x1
10
01

括號與邏輯運算子

在寫程式時,有時候會遇到很複雜的條件判斷,這時候就要善用括號來幫助我們整理程式碼,比如說「高中生或大學生如果是男生就可以參加籃球比賽」:

bool is_high_school_student, is_college_student, is_male;

if ((is_high_school_student || is_collge_student) && is_male) {
    cout << "You can join the basketball game!";
}

當然如果需要的話,也可以套更多層的括號。

陷阱

請看下面這段程式碼:

string s = "abc";
int n;
cin >> n;
if(s[n] == 'c' || s.length() > n)
cout << "OK";

你會發現這個程式如果輸入的 n 是 3,你或許會獲得 RE,這是因為字串 s 的 index 根本就沒有到 3,所以會獲得所謂「undefined behavior」的結果。

undefined behavior 就是指未定義的行為,C++ 不知道到底如何執行,在這種情況下,任何結果都有可能發生,包括程式當掉、輸出奇怪的結果甚至是讓程式永久卡住,所以要特別注意。

解決方法很簡單,只要把條件的順序調換一下就好了:

string s = "abc";
int n;
cin >> n;
if(s.length() > n || s[n] == 'c')
cout << "OK";

這樣 n < 3 時才會判斷第二個條件,這叫做「邏輯短路」,意思是當第一個條件已經可以確定結果時,就不會再去判斷第二個條件,這樣就可以避免 undefined behavior 的問題。

所以,在寫程式時,一定要注意邏輯運算子的順序,這樣才能避免一些不必要的問題。

小測驗

下列哪個敘述是正確的?