迴圈 while loop 和 for loop

接下來我們要講的觀念非常重要!迴圈(loop)是程式語言非常強大的功能之一,學會了他可以讓我們做到很多複雜的應用。如果沒有迴圈,程式碼會變得非常生硬且複雜,許多應用也會變為不可能。

迴圈基本上分為三種:

  1. while loop
  2. do while
  3. for loop

依據不同的情況,我們可能會使用不同的迴圈種類,但其實他們要實現的功能都一樣!接下來讓我們一個一個來講這三種迴圈的差別。

什麼是迴圈

在更深入討論三種迴圈之前,我們要先來了解到底什麼是迴圈以及他的功能是什麼!

首先我們用我們現有的知識來完成一個簡單的程式碼,這個程式碼的目的是要印出數字 1 到 1000。

#include <iostream>

int main()
{
    std::cout << "1" << std::endl;
    std::cout << "2" << std::endl;
    ...
    std::cout << "999" << std::endl;
    std::cout << "1000" << std::endl;
    return 0;
}

太棒了!我們用了一堆複製貼上花了大把時間終於寫好這個簡單的要命的程式了!這個簡單的程式碼很明顯的點出了我們面對的問題。那麼有沒有一種方法,可以讓我們快速的完成這個簡單的任務呢?

當然就是我們這章在討論的迴圈!迴圈就是一段在程式碼中只會出現一次,但是可以被連續執行多次的程式碼

聽起來很饒舌對不對?沒關係,讓我們馬上來看看實際的三種迴圈長什麼樣子!

while loop

第一種迴圈叫做 while loop,也是最簡單的一種迴圈。他長得和之前看到的 if 很像,後面都會接著一個條件式。我們馬上來用 while 來解決上面的問題!

#include <iostream>

int main()
{
    int number = 1;
    while (number <= 1000)
    {
        std::cout << number << std::endl;
        ++number;
    }
    return 0;
}

哇我們馬上讓前一段擁有一千多行的程式碼直接轉變為只有短短幾行的程式碼!讓我們來理解一下這是什麼意思吧。

首先我們定義了一個變數number並讓它為 1。下一行就是重點了,所有的 while 迴圈後面都會接著一個條件式,當這個條件式成立時,這個迴圈裡面的程式碼就會不斷地被重複執行。在這個例子中,我們的條件式就是number <= 1000。這個迴圈每執行一次,我們的變數number就會 +1。當number為 1001 時,條件式就不再成立,這時候就會跳出迴圈並結束程式。

那如果我們今天只要印出 100 個數字呢?我們就只需要迴圈的條件式為number <= 100就好啦!是不是很簡單呢!

巢狀 while loop

在前一章條件式 if-else我們有聊到巢狀 if(nested if)的概念。同樣的手法也可以用在 while 迴圈上!讓我們來看看以下範例,並試著想想看程式碼會輸出怎麼樣的結果吧!

#include <iostream>

int main()
{
    int i = 1;

    // 外圈總共會被執行 3 次
    while (i <= 3)
    {
        // 每執行外圈一次,內圈的程式碼就會被執行 i 次
        int j = 1;
        while (j <= i)
        {
            std::cout << j << ' ';
            j = j + 1;
        }

        std::cout << std::endl;
        i = i + 1;
    }
    return 0;
}

結果:

1
1 2
1 2 3

另外一個常見的用法就是用來製作 m * n 的程式地圖:

#include <iostream>

int main() {

    int m = 5;
    int n = 3;
    int i = 1;
    int j = 1;

    while (i <= m) {
        while (j <= n) {
            std::cout << "*  ";
            j = j + 1;
        }
        std::cout << std::endl;
        i = i + 1;
    }

    return 0;
}

結果:

*  *  *  
*  *  *  
*  *  *  
*  *  *  
*  *  *

do while

講完了 while 迴圈後,什麼是 do while 呢?其實從名字我們應該就可以猜到,do while 和 while 迴圈的功能大同小異。唯一的差別就在於 do while 在第一次進行條件審查之前,就會先無條件進行一次迴圈

有時候我們會希望程式先至少進行過一次,再來進行條件的審核。常見的例子是先讓使用者輸入資訊,再來依照使用者輸入的資訊來取決程式的下一步。

#include <iostream>

int main()
{
    int number;

    do
    {
        std::cout << "Please choose a number from 1-4." << std::endl;
        std::cin >> number;
    }
    while ((number > 4) || (number < 1));

    std::cout << "You choose number " << number << std::endl;

    return 0;
}

雖然上面的例子沒什麼意義,但這是一個簡單的 do while 使用的情境。

誒那聰明的你就會問了,難道我一定要使用 do while 才能實現這個功能嗎?其實不用!while 迴圈一樣也可以實現同樣的功能,只是程式碼就會變成大概以下這樣:

#include <iostream>

int main()
{
    int number = 2;

    while ((number > 4) || (number < 1))
    {
        std::cout << "Please choose a number between 1-4." << std::endl;
        std::cin >> number;
    }

    std::cout << "You choose number " << number << std::endl;

    return 0;
}

我們需要在創建變數number時就給予一個介於 1 到 4 之間的值,以確保會進入至少一次迴圈。但這樣會有一個問題,就是如果以後程式碼要求使用者輸入介於 5 到 10 的值時,我們就要給予number一個不同的值。這並不是什麼大問題,但對於程式設計來說,這就不是一個好的設計,因為會給未來的程式維護增加不確定因素。

但其實,do while 在現實中比較不常見,我認為一個大原因是因為人們不習慣條件式被放在一個迴圈之下。我自己也是幾乎沒用過 do while。更常見的方法其實是利用一個變數來控制條件式的成立與否。比如說:

#include <iostream>

int main()
{
    int number;
    bool flag = true;

    while (flag)
    {
        std::cout << "Please choose a number between 1-4." << std::endl;
        std::cin >> number;

        if ((number <= 4) && (number >= 1))
        {
            flag = false;
        }
        
    }

    std::cout << "You choose number " << number << std::endl;

    return 0;
}

雖然 do while 很少見,但我們仍然要知到有這個功能的存在!但現實生活中,建議還是能用 while 迴圈就用 while 迴圈啦!

for loop

現在讓我們來看最後一種迴圈,同時也是在 C++ 中最常見的一種!在編寫程式碼時,相比於前面提到的兩種迴圈,我們往往更偏好使用 for loop。其中的原因在於 for loop 更於簡單明瞭的定義起始及終止條件,因此在測試程式碼時也會更容易。

讓我們直接來利用 for loop 印出數字 1 到 1000 吧!

#include <iostream>

int main()
{
    for (int i = 1; i <= 1000; ++i)
    {
        std::cout << i << std::endl;
    }
    return 0;
}

是不是看起來非常簡單呢!接下來讓我們逐步分解 for loop 的架構。

我們可以看到,for 迴圈跟 while 迴圈一樣,後面都接著括號 ()。在這個括號裡面,我們可以依據分號 ; 將裡面的東西分成三個部分。

第一部分,我们有初始化 initialization。這是在迴圈開始時只發生一次的事情。通常用在設置變數並给與初始值。這些變數只有在迴圈內有效,從定義它的時刻開始一直存在到迴圈結束。接下来,我們看到的是迴圈條件 condition。每次迴圈跑完一次後都會檢查一次這個條件。如果條件為真,那麼就再執行迴圈一次。如果條件為假,則停止迴圈。最后,在執行完一次迴圈后,我們有结束語句 end。通常用在修改我們在初始化中定義的迴圈變數。在執行完結束語句後,我們回到迴圈條件,檢查是不是應該要再一次執行迴圈。

利用上面的例子的對應,我們的初始化就是 int i = 1,這裡我們建立了一個變數 i 並初始化它的值為 1。接下來我們看到迴圈條件  i <= 1000,代表在變數 i 小於等於 1000 之前我們都會一直執行這個迴圈。第三個我們看到結束語句 ++i,這代表每次迴圈結束,變數 i 都會被 +1。

理解完 for 迴圈後,整個程式邏輯就出來了!一開始變數 i 為 1 並進入迴圈,印出數字 1,迴圈結束,變數 i 加 1 變為 2。第二次迴圈開始,印出數字 2,迴圈結束,變數 i 再加 1 變為 3。第三次迴圈開始…,以此類推。直到迴圈結束執行 1000 次時,我們的變數 i 加 1 變為 1001,因為破壞迴圈條件  i <= 1000,因此整個迴圈結束,並執行 return 0;

更多 for loop 應用

雖然較常見的結束語句是將變數 +1,但是我們也可以將其設置為 +2,比如說:

for (int i = 1; i <= 1000; i+=2)

這樣輸出就會是 1 3 5 7 9 ...

或著我們也可以讓變數遞減,比如說:

for (int i = 1000; i >= 1; --i)

更特別的是,我們可以讓 for 迴圈的括弧為空,比如說:

for (;;)

雖然不常用,但這樣和 while (true) 的意義是相同的。

和巢狀 while loop 一樣,我們也可以有巢狀 for loop,比如說:

for (int i = 0; i < 10; ++i) {
    for (int j = 1; j <= 5; j+=2) {
        ...
    }
}

另外 for loop 還有一個進階功能,就是我們可以同時宣告並動用兩個變數,比如說:

for (int i = 0, j = 5; i < 5, j > 3; ++i, --j) {
    std::cout << i << ", " << j << std::endl;
}

因為變數 ij 需要同時滿足條件迴圈才會繼續,因此在變數 j 為 3 時,迴圈就會提前停止,所以上面程式的輸出就是:

0, 5
1, 4

看完這麼多 for 迴圈的應用,我們就知道為什麼相比於 while 迴圈,工程師會更傾向於使用 for 迴圈吧!因為 for 迴圈擁有更好的可塑空間,而且迴圈的停止條件、每個迴圈變數被加減了幾次等等資訊,都在一開始就非常清楚!