語法書 / AA 競程語法書 上冊 / 第七單元 / 判斷函式與短路求值

7.4 判斷函式與短路求值

函式的回傳型態其實可以是任何型態——intboolchardouble 都行,寫法都一樣:宣告好型態,再 return 一個那種型態的值。單就「換個回傳型態」而言沒什麼新東西好學;真正值得單獨談的是其中一種——回傳 bool 的函式。它專門回答一個「是不是」的問題(是奇數嗎?在範圍內嗎?),又叫判斷函式,是競程裡最常自己寫的一類函式,而且一旦放進 &&|| 裡,還會和第三單元的短路求值交互出新狀況,所以拉出來細講。

範例程式碼

#include<iostream>
using namespace std;

// 判斷 x 是否為奇數
bool isOdd(int x) {
    return x % 2 != 0;  // 比較的結果本來就是 bool,直接回傳
}

// 判斷 val 是否在 [low, high] 範圍內(含邊界)
bool isBetween(int val, int low, int high) {
    return low <= val && val <= high;
}

int main() {
    int x = -7;
    if (isOdd(x)) {
        cout << x << " is odd\n";
    }
    if (isBetween(5, 1, 10)) {
        cout << "5 is in [1, 10]\n";
    }

    return 0;
}

執行結果:

-7 is odd
5 is in [1, 10]

判斷函式的呼叫可以直接放進 if 的小括號裡——if (isOdd(x)) 唸起來就像一句話:「如果 x 是奇數」。條件複雜時,把它包成一個名字取得好的判斷函式,程式會比一長串比較運算好讀非常多。

(範例裡用 x % 2 != 0 而不是 x % 2 == 1,是因為負的奇數取餘會得到 -1——這個陷阱在第三單元常犯錯誤總覽介紹過。)

同一個判斷要用很多次?正是函式出場的時候

很多題目裡,同一個判斷會反覆出現。例如「牆有沒有擋在 A 和 B 之間」這件事,可能要對「起點到終點」和「起點到錘子」各判斷一次——把「點 w 是否在 pq 之間」寫成一個判斷函式,兩次判斷就都是一行呼叫,不必把同樣的比較邏輯想兩遍、寫兩遍(想錯一遍就 WA 了)。

短路求值遇上函式呼叫

第三單元 3.3 講過短路求值&& 左邊是 false 時右邊不會被計算;|| 左邊是 true 時右邊也不會被計算。當時條件裡放的都是變數比較,短路只是「少算一點」。但現在條件裡可以放函式呼叫了——短路的意思變成:右邊的函式根本不會被呼叫,函式裡的動作一件也不會發生。

#include<iostream>
using namespace std;

bool check(int x) {
    cout << "check(" << x << ") is called\n";
    return x > 0;
}

int main() {
    if (check(-1) && check(5)) {
        cout << "both true\n";
    }
    // check(-1) 回傳 false,&& 右邊的 check(5) 根本不會被呼叫

    return 0;
}

執行結果:

check(-1) is called

只印出一行——check(5) 完全沒被執行。判斷函式如果只是單純回傳 true / false,被短路跳過沒有任何影響;但若函式裡還會做別的事(印東西、修改變數),有沒有被呼叫到,結果就完全不同了。反過來,短路也是效能工具:把「很花時間的判斷」放在 && 的右邊,左邊不成立時就能整個省下來。