74 GP
[達(dá)人專欄] 跟著豬腳 C 起來:條件分歧,邏輯整個(gè)都出來了
作者:解凍豬腳│2020-09-17 20:04:41│巴幣:148│人氣:2443
前篇:跟著豬腳 C 起來:利用迴圈重複動(dòng)作,省時(shí)又省力
好久不見了,這裡是有生之年系列(誤)的《跟著豬腳 C 起來》。
上一次,也就是一年多前,我們講到了程式設(shè)計(jì)當(dāng)中體現(xiàn)電腦精髓的「迴圈」,也就是讓電腦去重複執(zhí)行指定好的一套動(dòng)作。
然而,單單使用「迴圈」也只不過是速度快而已,只有「快」並沒有什麼用。現(xiàn)實(shí)世界畢竟是複雜的,我們在做料理的時(shí)候,總得要知道哪些東西要切丁、哪些東西要切絲,總不是拿起菜刀來就朝著食材一陣瞎機(jī)巴亂砍。
這種時(shí)候我們就得讓電腦去判斷某些條件是否成立。這非常簡單,我們只要知道兩個(gè)單字就好:if 和 else。
就跟字面意思一樣,if 就是「如果」,else 就是「除此之外」。回顧上次說過的 for 迴圈用法,條件分歧也是「如果符合條件,就執(zhí)行大括號裡面的東西;如果不符合條件,就不執(zhí)行大括號裡面的東西」,也就是說:
if (條件 A) {
xxx;
}
只要符合條件 A,那電腦就會(huì)執(zhí)行 xxx,不符合的話這條 xxx 就會(huì)當(dāng)作沒看到。
但我們的需求不見得往往都是這麼簡單。有時(shí)候我們也許會(huì)需要在不符合狀況的時(shí)候也做些什麼,比如說「如果 1099736 是 3 的倍數(shù)的話,電腦告訴使用者 1099736 是 3 的倍數(shù);如果 1099736 不是 3 的倍數(shù)的話,電腦告訴使用者 1099736 不是 3 的倍數(shù)。」我們可以寫成:
if (1099736 % 3 == 0) {
printf("1099736 是 3 的倍數(shù)\n");
}
if (1099736 % 3 != 0) {
printf("1099736 不是 3 的倍數(shù)\n");
}
但這樣寫條件實(shí)在太麻煩了,而且即使 1099736 是 3 的倍數(shù),執(zhí)行完上面的條件後,電腦之後又會(huì)再計(jì)算一次 1099736 % 3 的值,造成不必要的效能浪費(fèi)。我們可以改寫成:
if (1099736 % 3 == 0) {
printf("1099736 是 3 的倍數(shù)\n");
} else {
printf("1099736 不是 3 的倍數(shù)\n");
}
如此一來,電腦最後一定只會(huì)走其中一條路,也不需擔(dān)心重複執(zhí)行的問題。
但老樣子,現(xiàn)實(shí)生活中的問題並不總是非黑即白,有的時(shí)候也許是個(gè)多向岔路口。比如說你今天想買車:財(cái)產(chǎn)達(dá)五千萬的時(shí)候,你可能會(huì)去買一部跑車;財(cái)產(chǎn)達(dá)三百萬、小於五千萬的時(shí)候,你可能會(huì)去買一部休旅車;財(cái)產(chǎn)達(dá)二十萬、小於三百萬的時(shí)候,你可能會(huì)去買一部二手的轎車;沒錢的時(shí)候,你就只能回家。
我們可以寫成:
int money = 20000000;
if (money >= 50000000) {
printf("買跑車\n");
} else if (money >= 3000000) {
printf("買休旅車\n");
} else if (money >= 200000) {
printf("買二手轎車\n");
} else {
printf("有夢最美\n");
}
這裡值得注意的是,程式的執(zhí)行順序都是由上往下,所以下面的 else if 都是建立在「先前的所有狀況都不成立」的前提上,我們自然也就沒必要把買休旅車的條件寫為「else if (money < 50000000 and money >= 3000000)」這麼麻煩了,後面的其他條件同理。
因此,我們在寫多條 else-if 的條件分歧的時(shí)候,可以優(yōu)先把最難達(dá)成的條件擺在最前面處理,這樣就能把這些反面條件累積起來利用了。
順帶一提,遇到這種很多個(gè) 0 的情況,可以用科學(xué)記號來取代,比如把 50000000 簡寫成 5e7,代表 5×10^7 的意思,但使用科學(xué)記號就要注意型別轉(zhuǎn)換,直接把它當(dāng)成整數(shù)來操作的話可能會(huì)遇上一些問題,比如說用 printf("%d\n", 5e3); 沒有辦法正常顯示 5000。
有了 if-else 的基本概念,假如我們想要寫一個(gè)身分證字號的驗(yàn)證器,那當(dāng)然就會(huì)需要告訴電腦「如果身分證字號的第一位不是英文字母,那就可以直接當(dāng)作無效的身分證字號」、「如果身分證字號的第一位數(shù)字不是 1 或 2」……諸如此類的條件。
這裡向海外的朋友解釋一下,臺(tái)灣的身分證字號格式是一個(gè)英文字母 + 九個(gè)數(shù)字,數(shù)字的第一位以 1 或 2 表示生理男性或女性,而每一位數(shù)字依照位置個(gè)別乘上固定的係數(shù)之後再加起來,如果可以被 10 整除就是有效的編號。
我們整理一下可以得到規(guī)則如下:
1. 身分證字號必須是 10 個(gè)字元
2. 第一個(gè)字元必須是英文字母
3. 第二個(gè)字元必須是數(shù)字 1 或 2
4. 按規(guī)則計(jì)算後的加權(quán)數(shù)值必須是 10 的倍數(shù)
也就是這樣的流程:
知道了流程,下一個(gè)問題自然就是該如何把它寫成 C 語言。按照身分證字號的判斷流程,可以知道它寫成 C 語言之後大概會(huì)長這樣:
if (長度不是 10 個(gè)字元) {
得到答案是無效
} else {
if (第一個(gè)字元不是英文字母) {
得到答案是無效
} else {
if (第二個(gè)字元不是 1 也不是 2) {
得到答案是無效
} else {
計(jì)算加權(quán)數(shù)值
if (加權(quán)數(shù)值不是 10 的倍數(shù)) {
得到答案是無效
} else {
得到答案是有效
}
}
}
}
這看起來非常複雜,更不用說如果程式設(shè)計(jì)師沒有適當(dāng)?shù)丶由峡s排,這 code 想必是亂成一團(tuán)(所以為什麼會(huì)強(qiáng)調(diào)縮排很重要),而身分證字號畢竟是要求每個(gè)條件都成立才能算是有效的,所以我們也可以把問題簡化成:
if (長度不是 10 個(gè)字元 或 第一個(gè)字元不是英文字母 或 第二個(gè)字元不是 1 也不是 2) {
得到答案是無效
} else {
計(jì)算加權(quán)數(shù)值
if (加權(quán)數(shù)值不是 10 的倍數(shù)) {
得到答案是無效
} else {
得到答案是有效
}
}
寫法可以有很多種,端看你怎麼去組合它。
接下來就是實(shí)作了。為了取得字元陣列內(nèi)容的長度,我們可以用「#include <string.h>」引入 string.h 標(biāo)頭檔,利用裡面的 strlen() 函式來取得 char[] 型態(tài)變數(shù)的長度:
char id[16]="test12345";
printf("%d\n", strlen(id)); // → 得到長度為 9
其實(shí)流程出來了,也就只剩下加減乘除、陣列的運(yùn)用和 char-int 之間的轉(zhuǎn)換而已。畢竟身分證的英文字母規(guī)律有點(diǎn)亂,為了不要在條件分歧以外的話題著墨太多,直接把 code 貼出來吧,細(xì)節(jié)就留給需要熟悉 chat-int 轉(zhuǎn)換的人慢慢看了:
#include <stdio.h>
#include <string.h>
int main(void) {
char id[16] = "A123456789";
int checkArr[16];
int checkMultiply[16] = {1, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1};
int checksum = 0;
// to uppercase
if (id[0] >= 97 && id[0] <= 122) {
id[0] -= 32;
}
// check the format
if (strlen(id) != 10 || id[0] < 65 || id[0] > 90 || (id[1]!='1' && id[1]!='2')) {
printf("無效的格式\n");
} else {
// prepare for calculation
if (id[0] >= 'A' && id[0] <= 'H') {
checkArr[0] = 1;
checkArr[1] = id[0]-'A';
} else if (id[0] == 'I') {
checkArr[0] = 3;
checkArr[1] = 4;
} else if (id[0] == 'J' || id[0] == 'K') {
checkArr[0] = 1;
checkArr[1] = id[0]-'J'+8;
} else if (id[0] >= 'L' && id[0] == 'N') {
checkArr[0] = 2;
checkArr[1] = id[0]-'L';
} else if (id[0] == 'O') {
checkArr[0] = 3;
checkArr[1] = 5;
} else if (id[0] >= 'P' && id[0] <= 'V') {
checkArr[0] = 2;
checkArr[1] = id[0]-'P'+3;
} else if (id[0] == 'W') {
checkArr[0] = 3;
checkArr[1] = 2;
} else if (id[0] == 'X') {
checkArr[0] = 3;
checkArr[1] = 0;
} else if (id[0] == 'Y') {
checkArr[0] = 3;
checkArr[1] = 1;
} else if (id[0] == 'Z') {
checkArr[0] = 3;
checkArr[1] = 3;
}
int i;
for (i=1; i<=9; i++) {
checkArr[i+1] = id[i]-'0';
}
// calculate the checksum
for (i=0; i<11; i++) {
checksum += checkArr[i]*checkMultiply[i];
}
// check the checksum
if (checksum % 10 == 0) {
printf("%s 為有效的身分證字號\n", id);
} else {
printf("%s 為無效的身分證字號\n", id);
}
}
return 0;
}
這裡補(bǔ)充一點(diǎn),「&&」可以表示條件句的「and」,「||」可以表示條件句的「or」,「!」可以表示條件句的「not」。
在絕大多數(shù)的程式語言當(dāng)中,你都可以看得到 if / else-if / else 的用法,只是可能語法會(huì)長得有點(diǎn)不一樣就是了。
如果你想判斷的對象非常單純,只有一個(gè)變數(shù),但有非常多種可能性的話,你還可以選擇不要使用 if 句法,而是 switch-case 句法。用法非常簡單,填入變數(shù)和值交給電腦去判斷就可以了:
switch (id[0]) {
case 'A': // 如果 id[0] == 'A'
printf("出生於臺(tái)北市\(zhòng)n");
break;
case 'B':
printf("出生於臺(tái)中市\(zhòng)n");
break;
case 'D':
printf("出生於臺(tái)南市\(zhòng)n");
break;
case 'E':
printf("出生於高雄市\(zhòng)n");
break;
case 'F':
printf("出生於新北市\(zhòng)n");
break;
case 'H':
printf("出生於桃園市\(zhòng)n");
break;
default:
printf("反正不是直轄市的就對了\n");
break;
}
要注意 switch-case 句法必須在執(zhí)行內(nèi)容的尾部加上一個(gè) break(如果沒有加上 break 的話就會(huì)繼續(xù)檢查或執(zhí)行其他的 case),而下面的 default 則相當(dāng)於 else,觀念上大同小異。
只要會(huì)了這兩種句法,程式寫起來就會(huì)變得很輕鬆,需要判斷多個(gè)條件就用 and、or 和小括號來組合、串接,達(dá)到想要的效果。至於如何把判斷的內(nèi)容和順序?qū)懙镁営制粒脱鲑噦€(gè)人的經(jīng)驗(yàn)累積了。
雖然如此,講到了條件分歧,不免還是要講一下所謂的「三元運(yùn)算子」。在 C 語言裡,如果你碰上了非常單純的「非黑即白」的狀況,可以使用三元運(yùn)算子來讓程式碼變得更簡短。
用法就是「A ? B : C」。代表的是「A 成立嗎?成立的話就 B,不成立的話就 C」。像前面的「有效」和「無效」之分,我們就可以直接把它從五行縮短成一行,利用三元運(yùn)算子來表示:
printf("%s 為%s的身分證字號\n", id, (checksum%10 == 0)?"有效":"無效");
只是這麼做就會(huì)犧牲掉部分的可讀性了,畢竟簡化得太多容易讓其他程式設(shè)計(jì)師一時(shí)看不懂你在寫什麼(e.g., 北車, 高火)。
截至目前為止,我們已經(jīng)講了如何顯示文字、如何操作變數(shù)、如何使用迴圈、如何一次宣告大量變數(shù)、如何使用條件分歧……有了這些,你就備齊了解決問題前需要的各項(xiàng)工具。
讓我們一起踏上偉大的爆肝之路吧。
引用網(wǎng)址:http://www.jamesdambrosio.com/TrackBack.php?sn=4919771
All rights reserved. 版權(quán)所有,保留一切權(quán)利
相關(guān)創(chuàng)作
同標(biāo)籤作品搜尋:第一次踏入墳場就上手|從入門到入墳|從入門到放棄|C語言|程式設(shè)計(jì)|C|C++|寫程式|程式|回收業(yè)者
留言共 27 篇留言
乃乃:
上一篇發(fā)文時(shí)我才剛要升大學(xué)
這篇發(fā)文時(shí)我已經(jīng)修完一學(xué)期的C了[e21]
09-17 20:08
解凍豬腳:
笑死 屌打獵人系列
09-17 20:08
曰光:
能不能在場外也這樣正常 噁噁噁
09-17 20:10
怕爆:
才華洋溢(錯(cuò)頻
09-17 20:14
雞塊:
初學(xué)很容易寫出if(15 <= x <= 16)這種code
每次看到都要中風(fēng)
09-17 20:18
解凍豬腳:
Python 是支援這種寫法的,真的很人性化,也很容易把人養(yǎng)慣 [e6]
09-17 20:23
美好的過去漸行漸遠(yuǎn):
是說如果是用C的話,就沒有class這個(gè)語法了吧?
09-17 20:18
解凍豬腳:
對,要到 C++,物件導(dǎo)向的概念才比較完整
09-17 20:25
你說的對:
卡晚點(diǎn)看
09-17 21:40
しろ:
if(小豬腳>30cm){
需要潤滑
}else{
我要進(jìn)去了
}
09-17 22:12
解凍豬腳:
void use687(void){ ... }
09-17 22:14
熾心凝:
現(xiàn)在看到else if都想寫成elif(python寫太多
09-18 17:02
poko兔:
純推舉例XD
03-26 12:26
我要留言提醒:您尚未登入,請先
登入再留言
74喜歡★johnny860726 可決定是否刪除您的留言,請勿發(fā)表違反站規(guī)文字。
前一篇:發(fā)現(xiàn)了喜歡的 Vtube...
後一篇:[達(dá)人專欄] 只靠圖片連...