基礎程式設計(8)-for迴路控制

從這一節開始進入程式設計的第二種流程控制方式:迴路控制。迴路控制的概念在生活中比較少見,簡單地說來就是”repeat”重複執行。不過在配合不同語法,例如 if/else 之後,可能就不是單純重覆一樣的事情了。這個部份比較複雜,常常是初學者的第一道大難關,但是一定要想辦法學會它,因為它可以替我們解決很多事情。

for迴圈基本概念

迴路控制最簡單的就是for迴圈,同時也是最常被使用的。當設計師”可以預期”某一段程式碼要”執行幾次”時,就可以使用for迴圈。只要是用數學算得出來執行次數的目標,都屬於可預期的範圍。舉個簡單的例子,當我們要算 10 加到 25 時,就可以用for迴圈。語法的格式與範例如下(適用於C/C++/Java):

int sum = 0;

//for ( 變數=初始值; 執行條件的比較運算; 變數更新算式)
for ( int i = 10; i <= 25; i++) {
    sum = sum + i;  //sum += i;
}

上面這段程式第 1 行先宣告了儲存加總結果用的 sum,接著開始執行for迴圈。在for迴圈第一次執行的時候,宣告變數 i = 10,接著執行第 5 行的加總,sum = 0 + 10 = 10。執行到第 6 行for的結尾時,會回到第 4 行繼續執行for迴圈,這時候已經是第二次執行了,所以電腦會先執行變數更新的 i++,於是 i 變成 11,再判斷執行條件 11 <= 25 成立,所以繼續往下執行。第二次執行第 6 行時,sum = 10 + 11 = 21,看到第 7 行for結尾再回到第 4 行……。就這樣一直重覆執行 4 ~ 7 行,直到 i <= 25 不成立,也就是 i = 26 為止。所以第十七次執行第 4 行時(25-10+1=16,第 5 行會執行 16 次),i = 25 + 1 = 26,i <= 25 不成立,於是程式直接跳到第 7 行結束for迴圈。迴圈結束的時候,i = 26。 這段程式碼如果使用BASIC語言描述,也許會更容易理解一些。BASIC語言是使用初始值 To 最終值的方式來定義for迴圈,不需要執行條件的比較運算。此外,BASIC語言可以定義 Step 表示變數每次增加的數量,如果沒有指定 Step,則預設值為 1。下面是範例的程式碼(適用於VB.NET):

Dim sum As Integer

'For 變數 = 初始值 To 最終值 Step 變數每次增加的數量
For i As Integer = 10 To 25 'Step 1
    sum += i
Next i

在for迴圈裡面執行的程式碼不一定要使用到for指定的變數(如上面範例的 i),程式設計師只要根據自己的需求使用即可。不過有一點要特別注意的是,如果在for迴圈裡使用運算式修改 i 的數值,則”會影響”迴圈的執行,一般來說不建議這樣做。另外,i 也可以是倒數的數值,只要結束條件比初始值來得小即可,這時通常使用「i–」或「Step -1」作為變數更新的方式。

在C風格的程式語言中,for迴圈裡可以定義多個變數,並且使用逗號(,)分隔;BASIC語言則只能有一個變數隨著for語法更新。兩種語法的應用範圍不一樣,可以參考接下來九九乘法表的範例。

九九乘法表

學習for迴圈最好自己寫過一次九九乘法表的程式。九九乘法表是一個二維的表格,所以我們可以使用巢狀for迴圈來完成它。巢狀的意思和前文if/else條件控制是一樣的,表示在一個for迴圈裡面還有另一個for迴圈。先使用BASIC語言做範例,參考的程式碼如下(適用於VB.NET):

Dim i, j As Integer

For i = 2 To 9
    For j = 1 To 9
        Dim ans As Integer = i * j
        Console.Write(i & "*" & j & "=" & ans & vbTab)  '輸出運算式後加Tab鍵 vbTab
    Next j
    Console.WriteLine() '換行
Next i

這裡的變數 i 是直行,變數 j 是横列。所以 i 的數值是由 2 到 9,j 則是 1 到 9。在這個巢狀迴圈中,i 迴圈每執行一次,j 迴圈就會執行九次;j 迴圈每次執行都會輸出 i * j 的結果,並且在結果後面加上Tab鍵來分隔。當 j 迴圈執行九次結束後會插入換行符號,並且開始執行下一個 i 迴圈。輸出結果參考如下:

九九乘法表

使用C風格程式語言撰寫時,由於執行條件和變數更新方式可以自由設定,所以我們可以使用一個for迴圈就完成九九乘法表。不過,這種方式會使程式不容易閱讀,所以不建議使用,這裡只是提供給讀者作為參考(以下為Java語法):

int i, j;
for (i = 2, j = 1; i < 10;
    i += (j + 1) / 10, j = (j % 9) + 1)
{
    int ans = i * j;
    System.out.print(i + "*" + j + "=" + ans + "\t");   //輸出運算式後加Tab鍵 \t

    if(j==9) {
        System.out.println();   //輸出換行
        //i++;
    }
}

從前面的BASIC語法我們可以知道 i 和 j 的初始值分別為 2、1,所以上面第 2 行for迴圈的語法中,第一個分號前面初始值設定為「i = 2, j = 1」。決定九九乘法表是否結束,主要是看 i 值執行到 9 為止,所以我們的執行條件只要設定「i < 10」即可,當 i 被加到 10 則結束迴圈。 接著要看的就是最複雜的第 3 行了,先從比較簡單的 j 開始。每一次執行輸出後 j 都要 +1,但是當它加到 9 之後又要回頭變成 1。所以我們可以利用整數除法求餘數的運算子,讓 j 無論怎麼加都不會超過 9。如果我們設定「j = (j + 1) % 9」,則 j 的值會在每次for迴圈 +1,但它的值是在 0 ~ 8 之間(9 除 9 餘數 0)。所以我們把 +1 移到後面,「j % 9」永遠是輸出 0 ~ 8 之間的數值,+1 後就變成 1 ~ 9 了。程式第一次執行這個運算式時(for迴圈第2次執行),j = (1 % 9) + 1 = 2,正好是我們要的數值。

把 j 解決之後就要來看 i 如何處理了。可以看到註解的部份,我們希望 i 要等 j = 9 且輸出完之後才 +1,所以我們不能在for迴圈寫 i++,而且 i 是否 +1 和 j 有關。既然要等 j = 9 才 +1,我們可以利用整數除法求商數的概念來完成判斷。在C風格程式語言中並沒有特別針對整數除法求商設定一個運算子,因為任何小數被指定給”整數“時,都只會留下商數。也就是說當我們寫「int i = j / 10」,表示如果 j < 10 則 i = 0。透過這種方式可以驗證 j 是否 =9。當 j = 8,(8 + 1) / 10 = 0,i 不必 +1 (= +0);當 j = 9,(9 + 1) / 10 = 1,i 要 +1。這裡我們可以看到「(j + 1) / 10」的結果就是 i 要增加的數值,所以我們直接使用加法指定,把運算的結果指定給 i 即可。

算到這裡初學者大概也已經頭昏眼花了。這裡只是讓大家看看不同的程式寫法,想學程式的人只要”看懂”就好,倒不必要求會寫這種程式。學習寫程式一定要知道程式是如何運行,執行到那個階段時,各種變數目前的數值是多少,這樣才能夠debug。這也是為什麼 Visual Studio 的除錯模式會幫我們把每個變數值顯示出來的原因。只是大部份的程式開發工具沒有那麼高級,所以基本功還是要會才行。

for迴圈的debug

for迴圈的重點就是要設定初始值、結束條件和更新方式,只要三者確定無誤,大致上就沒有什麼問題了。看看迴圈裡的程式碼是否會使用到迴圈上的變數,如果不會用到,那麼只要確定迴圈執行的次數正確即可。如果程式內會使用到for迴圈所設定的變數,就要檢查變數與程式的關係。以九九乘法表的例子來說,就是要確保 i 是 2.3.4.5…9 依序產生,而 j 則是 1.2.3…9 不斷重覆等等。for迴圈上的變數常常被用來作為”索引值”,輸出第一筆資料、第二筆資料…這個時候就要注意變數必須在索引值的範圍內才行,不可以叫電腦輸出不存在的第零筆資料。在未來的範例教學中會有機會談到這個部份。