7.4 判斷函式與短路求值
函式的回傳型態其實可以是任何型態——int、bool、char、double 都行,寫法都一樣:宣告好型態,再 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 是否在 p、q 之間」寫成一個判斷函式,兩次判斷就都是一行呼叫,不必把同樣的比較邏輯想兩遍、寫兩遍(想錯一遍就 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,被短路跳過沒有任何影響;但若函式裡還會做別的事(印東西、修改變數),有沒有被呼叫到,結果就完全不同了。反過來,短路也是效能工具:把「很花時間的判斷」放在 && 的右邊,左邊不成立時就能整個省下來。