2016年5月8日 星期日

給新手的C++教學 (上冊) - 7. 讓程式碼看起來更簡單 (Simplify the code)

回到「給新手的C++教學 (上冊)」

上一章

上一章有提到,利用前6章所學,已經可以完全掌控電腦了

現在,我想問你,能否寫出一個程式,完成以下任務呢?
輸入一個字元,請判斷這個字元是不是「小寫母音字母 (a、e、i、o、u)」,是的話輸出「Yes」,否則輸出「No」(甚麼是字元?複習一下第2章吧~)

題外話:
怎麼表示「一個字元 (charactor),代表小寫字母『a』」呢?
不知道沒關係,因為之前的章節沒有提到XD
答案是「'a'」(用兩個「'」框住你想表達的字元)
這個表示法雖然是隨手提及,但是非常重要,之後的教學只要看到兩個「'」框起來的東西,你都要把它當作「一個字元」來看待

參考解答:

#include<cstdio>
int main()
{
    char a;
    scanf("%c",&a);
    if(a=='a')
    {
        printf("Yes\n");
    }
    else
    {
        if(a=='e')
        {
            printf("Yes\n");
        }
        else
        {
            if(a=='i')
            {
                printf("Yes\n");
            }
            else
            {
                if(a=='o')
                {
                    printf("Yes\n");
                }
                else
                {
                    if(a=='u')
                    {
                        printf("Yes\n");
                    }
                    else
                    {
                        printf("No\n");
                    }
                }
            }
        }
    }
    return 0;
}

你可能會發現:程式好冗長啊
有沒有簡單一點的寫法呢?
會這樣問,答案當然是「有」啊

可以發現,有很多的大括號 (「{」和「}」) 包起來的部分都只有一行程式碼 (「printf("Yes\n");」或「printf("No\n");」)
在這種情況下,大括號其實是可以省略的

所以,程式碼也可以這樣寫,完全不影響程式的功能:

#include<cstdio>
int main()
{
    char a;
    scanf("%c",&a);
    if(a=='a')
        printf("Yes\n");
    else
    {
        if(a=='e')
            printf("Yes\n");
        else
        {
            if(a=='i')
                printf("Yes\n");
            else
            {
                if(a=='o')
                    printf("Yes\n");
                else
                {
                    if(a=='u')
                        printf("Yes\n");
                    else
                        printf("No\n");
                }
            }
        }
    }
    return 0;
}

另外,一個「if」和一個「else」是配對起來的,不可分離
因此,我們甚至也可以省略將它們包起來的大括號!
廣泛的說,如果大括號裡面包起來的程式碼是不可分離的
那麼這個大括號就可以省略

這樣一來,程式碼又可以簡化成以下這樣了:



#include<cstdio>
int main()
{
    char a;
    scanf("%c",&a);
    if(a=='a')
        printf("Yes\n");
    else
        if(a=='e')
            printf("Yes\n");
        else
            if(a=='i')
                printf("Yes\n");
            else
                if(a=='o')
                    printf("Yes\n");
                else
                    if(a=='u')
                        printf("Yes\n");
                    else
                        printf("No\n");
    return 0;
}

還可以更簡單嗎?當然可以!
對電腦來說,C++程式碼中,若有連續的一個或多個空白 (' ')、Tab ('\t') 或換行 (’\n’) 的字元出現,效果就等於一個空白!

那你可能就會問:這樣不就可以把所有的程式碼通通寫在同一行了嗎?
的確是的!
不過,「#」這個符號開頭的程式碼 (如:#include<cstdio>) 比較特殊,它們和我們目前關心的部分格格不入,「永遠」不能寫在同一行

就算如此,我們還是可以把剩下的通通寫成一行:

#include<cstdio>
int main() { char a; scanf("%c",&a); if(a=='a') printf("Yes\n"); else if(a=='e') printf("Yes\n"); else if(a=='i') printf("Yes\n"); else if(a=='o') printf("Yes\n"); else if(a=='u') printf("Yes\n"); else printf("No\n"); return 0; }

這樣的程式還是可以正常執行的,功能也完全正常,很神奇吧!
但是,身為程式設計師,你真的覺得這樣寫比較好嗎?
一般人大概都會覺得「眼花撩亂」吧XD

如何把程式碼寫到清晰易懂,也是一門很重要的課題
要把程式碼寫到清晰易懂,有很多種方法
像這種利用「C++接受多餘空白、Tab、換行的特性」來整理程式碼的方式,我們稱為「排版 (Formatting)」
這是其中一種方法,也是最重要的方法
其他方式就看個人習慣

例如上面這個例子,我習慣寫成這樣:

#include<cstdio>
int main()
{
    char a;
    scanf("%c",&a);
         if(a=='a') printf("Yes\n");
    else if(a=='e') printf("Yes\n");
    else if(a=='i') printf("Yes\n");
    else if(a=='o') printf("Yes\n");
    else if(a=='u') printf("Yes\n");
    else            printf("No\n");
    return 0;
}

或這樣:

#include<cstdio>
int main()
{
    char a;
    scanf("%c",&a);
    if(a=='a') printf("Yes\n");
    else if(a=='e') printf("Yes\n");
    else if(a=='i') printf("Yes\n");
    else if(a=='o') printf("Yes\n");
    else if(a=='u') printf("Yes\n");
    else printf("No\n");
    return 0;
}

你也可以有自己的風格,只要可以讓程式碼看起來舒服就好

另外,像這種很多情況下都要做同一件事的 (printf("Yes\n");),我們可以把它們寫在同一個「if」裡面
詳細方式請參考以下程式碼:

#include<cstdio>
int main()
{
    char a;
    scanf("%c",&a);
    if(a=='a'||a=='e'||a=='i'||a=='o'||a=='u') printf("Yes\n");
    else printf("No\n");
    return 0;
}

其中你會看到新朋友「||」,它代表「或者」的意思
把每個條件中間用「||」連接起來
這樣一來,只要其中一個條件成立,「if」裡面的程式碼就會被執行
否則就會執行「else」裡面的程式碼 (如果「else」存在的話)

不知道有沒有人是像下面這樣的邏輯寫出這個題目呢?

#include<cstdio>
int main()
{
    char a;
    scanf("%c",&a);
    if(a!='a')
    {
        if(a!='e')
        {
            if(a!='i')
            {
                if(a!='o')
                {
                    if(a!='u')
                    {
                        printf("No\n");
                    }
                    else
                    {
                        printf("Yes\n");
                    }
                }
                else
                {
                    printf("Yes\n");
                }
            }
            else
            {
                printf("Yes\n");
            }
        }
        else
        {
            printf("Yes\n");
        }
    }
    else
    {
        printf("Yes\n");
    }
    return 0;
}

「如果每一個母音都和它不相等,就輸出『No』,否則輸出『Yes』」
這樣也是可以的
你甚至該為自己感到高興,因為你想到了跟別人不一樣的方法!

仔細分析一下程式碼
很多情況下也都要做同一件事 (printf("Yes\n");),我們能不能也只用一個「if」就解決呢?
之前的例子是「只要其中一個條件成立就好」,我們用「||」連接
而這個例子是「所有的條件都要成立才行」,我們就用「&&」連接

因此,以上的程式碼可以改成下面這樣,程式的邏輯依然沒有任何改變:

#include<cstdio>
int main()
{
    char a;
    scanf("%c",&a);
    if(a!='a'&&a!='e'&&a!='i'&&a!='o'&&a!='u') printf("No\n");
    else printf("Yes\n");
    return 0;
}

「&&」代表「而且」的意思
把每個條件中間用「&&」連接起來
這樣一來,所有的條件「都要」成立,「if」裡面的程式碼「才會」被執行
否則就會執行「else」裡面的程式碼 (如果「else」存在的話)

前後比較一下,你是不是發現程式碼變得簡單許多呢?

還記得「判斷閏年」那題嗎?(請見第4章)
你有辦法把它改成「只需要一個『if』」的版本嗎?

參考解答:

#include<cstdio>
int main()
{
    int year;
    scanf("%d",&year);
    if(year%4!=0||(year%100==0&&year%400!=0)) printf("不是閏年\n");
    else printf("是閏年\n");
}

你說你不知道「||」和「&&」也可以使用小括號?
拜託,這不是很「trivial」嗎?

下一章

感謝:
(版權所有 All copyright reserved)

10 則留言:

  1. 哈囉,我是C語言初學者,今天如果這樣
    if (k == 0)
    sum = -1;
    else{
    if(){}
    else{}
    if(){}
    else{}
    }
    請問這時最外面那個else的"{}"能省去嗎?謝謝!

    回覆刪除
    回覆
    1. 哈囉~
      不能哦
      因為在最外面那個else的"{}"裡面有兩組if-else的配對
      這兩組if-else是可以拆開的(就是拆開後不會造成某個else沒有if可以配對之類的語法錯誤)
      因此如果把最外面那個else的"{}"省去
      會變成只有第一組if-else在最外面那個else裡面哦

      刪除
  2. 其實我是陳奕均啦XD,謝謝你的回答,我再問一個,while(){
    }
    這大括號跟if(){
    }
    這個都能省略,邏輯上相同對不對?

    回覆刪除
    回覆
    1. 嗨!奕均
      大括號省略後邏輯要相同的話,大括號裡面的程式碼就必須是不可拆開的哦
      不可拆開就是指一段程式碼無法切成兩半,並且這兩半程式碼可以各自獨立存在
      如果沒有信心的話,可以自己寫出程式碼測試看看~
      當然,把大括號留著是最保險的做法,千萬不要為了少打幾個字導致最後花了一整天找錯誤和猜錯誤!

      刪除
    2. 所以能不能省略要看情況,看你的大括號裡面放了甚麼

      刪除
  3. 想問一下printf scanf
    與cin cout 差別在哪裡呢?
    之前課本內是教後者 也說後者是比較適合C++的
    前者是比較適合C
    請問哪個比較方便實用呢??
    還是習慣後沒甚麼差?

    回覆刪除
    回覆
    1. 唔,據說官方推薦的是cin/cout,不過可能小莫特別習慣用scanf/printf、覺得格式容易掌控得多、程式碼寫起來也很直覺吧,這篇教學才使用了scanf/printf
      應該是習慣後沒什麼差,不過小莫對cin/cout其實沒那麼熟所以也不敢保證XD
      以下歡迎各位大神們來討論和比較~(?

      刪除
  4. 你好,感謝版主的介紹與分享,很實用
    但有一點說明想請教確認是否為筆誤
    在本頁內容的最後關於閏年計算的code中顯示:
    if(year%4!=0||(year%100==0&&year%400!=0)) printf("不是閏年\n");
    是否 "year%400!=0" 應改為 "year%400==0" ??
    這邊有點疑惑 謝謝!

    回覆刪除
    回覆
    1. 不行,應為他判斷的是:"不是閏年"

      刪除
    2. 每400年不閏哦,所以剛好400年的時候反而不是閏年,可以想一下XD
      例如說1600年、2000年、2400年都不是閏年

      刪除

歡迎留言或問問題~
若您的留言中包含程式碼,請參考這篇
如果留言不見了請別慌,那是因為被google誤判成垃圾留言,小莫會盡快將其手動還原

注意:只有此網誌的成員可以留言。