ETH官方钱包

創(chuàng)作內(nèi)容

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 篇留言

Pekocado
霸...

09-17 20:06

純淨(jìng)好水
我愛豬腳,謝謝教學(xué)

09-17 20:06

兆C
C

09-17 20:07

乃乃
上一篇發(fā)文時(shí)我才剛要升大學(xué)
這篇發(fā)文時(shí)我已經(jīng)修完一學(xué)期的C了[e21]

09-17 20:08

解凍豬腳
笑死 屌打獵人系列09-17 20:08
曰光
能不能在場外也這樣正常 噁噁噁

09-17 20:10

北極熊
有生之年系列

09-17 20:12

美好的過去漸行漸遠(yuǎn)
好電ㄛ

09-17 20:13

怕爆
才華洋溢(錯(cuò)頻

09-17 20:14

通知達(dá)人
我也要C啦

09-17 20:16

雞塊
初學(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 20:26

Weiiiiiii
教ㄇ...

09-17 20:58

G家遊民
\csy/

09-17 21:38

你說的對
卡晚點(diǎn)看

09-17 21:40

福利熊送幸福
這篇淺顯易懂 推推

09-17 21:49

しろ
if(小豬腳>30cm){
需要潤滑
}else{
我要進(jìn)去了
}

09-17 22:12

解凍豬腳
void use687(void){ ... }09-17 22:14
夫風(fēng)若雪
感謝分享~

09-17 22:41

先生不要這樣好嗎
本來以為這系列停更了

09-17 22:46

??求出處學(xué)術(shù)用??
我還以為到程式版

09-17 22:57

Waterlow
我要追到講完c的所有函式庫

09-18 06:37

雪之王女?F?巧可奈
外行純推

09-18 08:50

熾心凝
現(xiàn)在看到else if都想寫成elif(python寫太多

09-18 17:02

繁華-POOR THINGS
感謝大佬 我google||意思才看到你有補(bǔ)充 太貼心了吧(′▽`)

09-18 18:12

odie_ou
?:

09-21 00:12

義大利麵加麵
C

09-23 17:11

poko兔
純推舉例XD

03-26 12:26

我要留言提醒:您尚未登入,請先登入再留言

74喜歡★johnny860726 可決定是否刪除您的留言,請勿發(fā)表違反站規(guī)文字。

前一篇:發(fā)現(xiàn)了喜歡的 Vtube... 後一篇:[達(dá)人專欄] 只靠圖片連...

追蹤私訊切換新版閱覽

作品資料夾

------------------ (0)

豬腳生活 (1)
日常雜談、巴哈大小事 (194)
煞氣a國中生 (7)
高中生活日誌 (55)
大學(xué)生活日誌 (34)
冬令營回憶錄 (19)
也許藏有一些小祕密吧? (3)
各式各樣的開箱文 (11)
貓科動(dòng)物時(shí)間 (15)

------------------ (0)

繪圖創(chuàng)作 (1)
電繪插圖、草稿 (199)
短篇漫畫、單幅標(biāo)語 (61)
上課太無聊的手繪塗鴉 (8)
不知道該怎麼分類的綜合作品 (18)

文字創(chuàng)作 (1)
草莓兵的國軍紀(jì)實(shí) (14)
我與らい的點(diǎn)點(diǎn)滴滴 (12)
那些榮耀的時(shí)刻與心跳加速的瞬間 (60)
有感而發(fā)的隨筆之作、無法分類的短文 (17)

------------------ (0)

語言學(xué)習(xí) (1)
日語:天気がいいから (5)
粵語:唔好再淨(jìng)係識(shí)講粗口喇 (6)
英語:Hey, you! (1)

程式設(shè)計(jì)及電腦網(wǎng)路 (1)
系列文:跟著豬腳 C 起來 (10)
系列文:論壇網(wǎng)站運(yùn)作原理 (3)
Rust (7)
Go(Golang) (11)
Ruby / RGSS (7)
Visual Basic (13)
JavaScript (1)
各種原理 (17)

思想:多思考一下,世界會(huì)更不一樣 (1)
網(wǎng)路經(jīng)驗(yàn)、社會(huì)觀察 (23)
檸檬庫 (21)

數(shù)學(xué):我來拯救你的期中考了 (1)
各類基礎(chǔ)觀念 (5)
國中生也能懂的微積分 (9)
微分方程 (0)

小說:用筆鋒劃出新世界的入口 (1)

繪圖:我也想畫出私巴拉西的美圖 (0)
擺脫廉價(jià)感的九種方法 (3)
兄弟,一起畫圖嗎? (7)
未分類繪圖筆記 (7)

------------------ (0)

施工中 (22)

不堪回首的痕跡、雜物堆放 (31)

------------------ (0)

未分類 (0)

e975483216琳太跟水亞
BENNA被她男友打 哈哈哈哈哈 笑死人 讚 NICE看更多昨天21:51


face基於日前微軟官方表示 Internet Explorer 不再支援新的網(wǎng)路標(biāo)準(zhǔn),可能無法使用新的應(yīng)用程式來呈現(xiàn)網(wǎng)站內(nèi)容,在瀏覽器支援度及網(wǎng)站安全性的雙重考量下,為了讓巴友們有更好的使用體驗(yàn),巴哈姆特即將於 2019年9月2日 停止支援 Internet Explorer 瀏覽器的頁面呈現(xiàn)和功能。
屆時(shí)建議您使用下述瀏覽器來瀏覽巴哈姆特:
。Google Chrome(推薦)
。Mozilla Firefox
。Microsoft Edge(Windows10以上的作業(yè)系統(tǒng)版本才可使用)

face我們了解您不想看到廣告的心情? 若您願(yuàn)意支持巴哈姆特永續(xù)經(jīng)營,請將 gamer.com.tw 加入廣告阻擋工具的白名單中,謝謝 !【教學(xué)】