第 6 章 流程控制 ( 二 ): 迴圈 著作權所有 旗標出版股份有限公司 1 本章提要 6-1 for 迴圈 6-2 while 迴圈 6-3 do/while 迴圈 6-4 巢狀迴圈 6-5 變更正常流程的 break 與 continue 6-6 綜合演練 2 1
認識迴圈 迴圈是用來解決重複性工作 ( 重複的執行動作 ) 的敘述 在日常生活中, 往往我們都會為了一些例行性 (routine) 的工作浪費許多時間 ; 辦公人員每天重複的收發表格 操作員重複地把原料放到機器上等 這種重複性的工作即使是在寫程式時也很容易發生, 此時我們就需要利用迴圈來解決此類程式問題 3 認識迴圈 比如說我們要設計一個從 1 加到 100 的程式, 並將每次累加的結果都輸出到螢幕上, 如果不使用迴圈, 程式寫出來可能會像下面的樣子 : 4 2
認識迴圈 5 認識迴圈 上列程式中, 我們浪費太多的時間在撰寫重複的程式碼 如果程式要算的是 1 加到 1000, 甚至 1 加到 10000 呢? 此時將耗費許多不必要的時間在程式撰寫上面 為了改進重複性程式的撰寫或執行效率, Java 提供了數種 迴圈 敘述, 讓我們可大幅簡化重複性程式的撰寫 6 3
認識迴圈 迴圈其實是利用條件運算式的 true/false 來判斷是否要重複執行迴圈內的動作敘述, 當條件運算式為 true, 程式才會執行迴圈內的動作 ; 條件運算式為 false 時, 就會結束迴圈 ( 跳出迴圈 ), 然後繼續往下執行 如下圖 : 7 認識迴圈 8 4
認識迴圈 當使用迴圈來解決上述 1 加到 100 的程式問題時, 程式便會精簡許多, 如下 : 9 認識迴圈 10 5
認識迴圈 程式碼是不是精簡很多了呢? 那是因為重複的程式碼被我們用迴圈取代了, 只需寫一次即可, 不必重複寫很多次, 這就是迴圈的妙用 其中 for 迴圈的用法我們會在下一節中詳細的說明 在 Java 語言中, 共有 for while 及 do 三種迴圈敘述 以下我們將分別解說此三類迴圈並實際演練 11 6-1 for 迴圈 在前面的範例中, 我們使用了 for 迴圈來計算 1 加到 100 的數值 而 for 迴圈就是適用在需要精確控制迴圈執行次數的場合, 像上述的例子, 我們就是控制迴圈加到 100 就不要再執行了, 此時程式便跳出迴圈 以下就來說明 for 迴圈的語法 12 6
語法 for 迴圈的語法如下 : 初始運算式 : 在第一次進入迴圈時, 會先執行此處的運算式 在一般的情況下, 我們都是在此設定條件運算式中會用到的變數之初始值 13 語法 條件運算式 : 用來判斷是否應執行迴圈中的動作敘述, 傳回值需為布林值 此條件運算式會在每次迴圈開始時執行, 以重新檢查讓迴圈執行的條件是否仍成立 比方說, 條件運算式為 i < 10, 那麼只有在 i 小於 10 的情形下, 才會執行迴圈內的動作敘述 ; 一旦 i 大於 10 時, 迴圈便結束 14 7
語法 控制運算式 : 每次執行完 for 迴圈中的動作後, 就會先執行此運算式 此運算式通常都是用於調整條件運算式中會用到的變數值 15 語法 以上例來說, 在條件運算式為 i < 10 的情況下, 要讓此迴圈可以執行十次, 可在初始運算式設定 i=0, 再利用控制運算式來改變 i 值, 例如 i++, 讓 i 以每次加 1 的方式不斷累加, 等到 i 的值為 10 時, 條件運算式 i < 10 的結果為 false, 此時迴圈結束也完成迴圈執行 10 次的目標 16 8
語法 迴圈動作敘述 : 將您希望利用迴圈重複執行的敘述放在此處, 如果要執行的敘述只有一個, 也可省略前後的大括弧 {} 整個 for 迴圈的執行流程圖如右 : 17 執行流程 在上一小節中曾提及, for 迴圈一般都是用一個變數來決定執行的次數, 我們就以 for (i=0;i<3;i++) 這個迴圈為例, 來看看 for 迴圈的執行步驟 : 18 9
執行流程 由上述例子可知, 我們只要善用迴圈的條件運算式及控制運算式, 就可以控制迴圈的執行次數 由此亦可得知, 如果我們需要控制程式執行的次數, for 迴圈將是最好的選擇 我們實際以一個例子來作說明, 假設我們想要計算某個範圍內 ( 如 1 ~ 1000) 的所有奇數和, 此時使用 for 迴圈來解決此類問題是最洽當的 程式碼如下 : 19 執行流程 20 10
執行流程 21 執行流程 22 11
6-2 while 迴圈 while 迴圈的結構和前一章介紹的 if 條件判斷式看起來很類似, 兩者都有個用括號括住的條件運算式, 加上一組由大括號括住的的動作敘述 但兩者的差異在於 : while 迴圈每次在執行完大括號中的動作敘述後, 會跳回 while 的條件運算式再次檢查, 如此反覆執行, 直到條件運算式為 false 時才停止執行迴圈的動作 23 語法 while 迴圈有別於 for 迴圈的地方在於, while 迴圈不需要初始算值及控制運算式, 只需要條件運算式即可 如下述 : 24 12
語法 while: 當.. 的意思 會根據條件式的真假, 來決定是否執行迴圈內的動作 也就是說 當條件式結果為真, 就執行迴圈內的動作的意思 若為假, 則不予執行 ( 跳出迴圈 ) 條件運算式 : 可以是任何結果為布林值的運算式或布林變數 25 執行流程 26 13
執行流程 看完上述的流程圖, 應該不難發現, 其實 while 迴圈的執行與 for 迴圈十分類似 我們以上述計算奇數和的例子為藍圖, 用 while 迴圈來改寫, 並將奇數和改為偶數和, 結果如下 : 27 執行流程 28 14
執行流程 29 執行流程 其中第 23 行的 i+=2; 有點類似 for 迴圈的控制運算式, 讓 while 迴圈的條件運算式有可能產生 false 的結果 30 15
執行流程 如果把這行敘述拿掉, while 迴圈內的條件運算式狀況將不會有所改變 ( 永遠是 true), 此時程式就會一直重複執行迴圈的敘述, 而不會停下來, 這種情況稱之為無窮迴圈 假設要設計一個程式, 計算 1 到任意數值間, 所有整數的和, 而且在畫面上顯示從 1 開始加的時候, 每一次累加的和 如果使用 while 卻沒有敘述去改變條件運算式中用到的數值, 就會發生計算結果不正確的情況 : 31 執行流程 32 16
執行流程 33 執行流程 34 17
執行流程 我們可以發現這個程式有 2 個問題 : 首先, 我們希望程式算的是 0+1 再加 2 再加 3 的情形, 但上列程式卻不是這樣加 ; 其次, 這個迴圈會不停執行下去, 因為 i 值恆等於 1, 自然永遠都小於使用者輸入的 range 指定數值, 所以不會停止 我們必須在程式第 24 行放 i++; 或 i+=1; 這樣的敘述 : 35 執行流程 加到後面, i 值就會以累加 1 的方式不斷變動, 最後當 i 值大於 range 指定變數時就會停止迴圈 而且這樣一來, 程式顯示的執行結果也會變得正確 : 36 18
執行流程 37 6-3 do/while 迴圈 在三種迴圈方式裡, do/while 迴圈算是 while 的一種變型, 但它和 while 及 for 迴圈的執行流程上也有一些差異 前面介紹的 for 及 while 迴圈通常被稱為預先條件運算式迴圈 也就是說在執行迴圈之前, 會預先檢查條件式是否為真, 是的話才執行迴圈內的動作敘述 38 19
do/while 迴圈 do/while 迴圈則不同, 在第一次進入迴圈時, 不會先做任何檢查, 而是先執行完迴圈內的動作敘述後, 再檢查條件式是否成立所以 do/while 迴圈的特點就是 : 不論條件式為何, 迴圈敘述至少都會執行一次 39 語法 do/while 迴圈的結構像是個倒過來的 while 迴圈, 也就是把 "while ( 條件運算式 )" 這一段內容移到迴圈的最後面 : 40 20
執行流程 正如前面所提及的, do/while 是一個先執行迴圈內的敘述再進行條件運算式判斷的迴圈敘述, 因此其執行流程也稍稍有所不同, 如右 : 41 執行流程 以實例來測試, 可清楚的觀察到相同的程式因為執行流程上的不同, 所產生的結果也會不同的情況, 先用 while 迴圈來設計, 如下 : 42 21
執行流程 此範例程式的 while 迴圈會在每次做完條件檢查時, 將 i 的值加 1, 結果使迴圈的敘述會執行 3 次 接著用 do/while 來改寫同一個程式, 如下 : 43 執行流程 44 22
執行流程 如上, 由於 i 是從 0 開始, 且 do/while 會先執行完一次迴圈內的動作後, 才進行檢查, 使得這次迴圈的動作比前一個範例多執行 1 次 讀者在設計程式時, 可依程式的特性選用適當的迴圈迴圈敘述 45 6-4 巢狀迴圈 在上述的迴圈範例中, 我們都是以一維的方式去思考, 比如說 1 加到 100 此類一個累加變數就能解決的問題 但是如果我們想要解決一個像九九乘法表這種二維的問題 (x,y 兩累加變數相乘的情況 ) 就必須將使用迴圈的方式做一些變化, 也就是使用巢狀迴圈 (nested loops) 46 23
巢狀迴圈 簡單的說. 巢狀迴圈就是迴圈的大括號之中, 還有其它迴圈 例如 for 迴圈中還有 for 迴圈, 或是 while 迴圈等 以下就用實例來說明巢狀迴圈的應用 : 47 巢狀迴圈 48 24
巢狀迴圈 49 巢狀迴圈 在上述的例子中, 我們利用兩個迴圈分別來處理九九乘法表的 (x, y) 變數的累加相乘動作 當 x 等於 1 時, 必須分別乘以 1 到 9 的 y; 當 x 等於 2 時, 又是分別乘上 1 到 9 的 y..., 也就是在外部的 for 迴圈每執行一輪時, 內迴圈就會執行 9 次, 以此類推 執行流程如下圖 : 50 25
巢狀迴圈 51 巢狀迴圈 52 26
6-5 變更正常流程的 break 與 continue 有兩個敘述 :break 及 continue, 都可以忽視迴圈正常的執行流程, 而終止執行迴圈或跳出該輪迴圈 53 跳出一層迴圈的 break 如同 switch 多條件分支可以利用 break 敘述改變程式流程一樣, 迴圈一樣有 break 敘述可以用來改變迴圈執行的流程 迴圈中的 break 敘述, 與 switch 的 break 敘述有相同的功能, 它會中斷目前的迴圈執行, 或者說是 跳出 迴圈 54 27
跳出一層迴圈的 break 寫過數個迴圈程式後, 我們可以試著使用 break 來跳出迴圈 : 當程式中遇到某種狀況時, 而不要繼續執行迴圈, 即可用 break 來中斷迴圈 方法如下 : 55 跳出一層迴圈的 break 56 28
跳出一層迴圈的 break 由於在第 07 行的條件運算式 "i > 0" 恆為真, 所以會變成一個無窮迴圈 不過由於程式在實際執行時, i 變數會持續累加, 等累加到 i 的值等於 5 時, 第 10 行的 if 條件運算式其值為真, 所以會執行 break 來跳出此層迴圈 57 結束這一輪迴圈的 continue 除了 break 敘述外, 迴圈還有一個 continue 敘述, continue 的功能與 break 相似, 不同之處在於 break 會跳出整個迴圈, continue 僅跳出 這一輪 的迴圈, 請看以下的程式示範 : 58 29
結束這一輪迴圈的 continue 59 結束這一輪迴圈的 continue 執行結果獨缺了 5, 是因為第 5 圈時, 在程式執行第 8 行的 continue 後, 迴圈就被結束掉了, 直接進行第 6 圈的迴圈, 導致第 9 行敘述未被執行 60 30
標籤與 break/continue 敘述 若程式中用的是巢狀迴圈, 而需要程式在某種狀況下, 同時中止每一層的迴圈 或是同時中止該輪的內 / 外迴圈, 用單純的 break/continue 敘述顯得不方便, 因為必須在每一層的迴圈中, 都加上 break/continue 敘述, 才能跳出迴圈或中止該輪的內 / 外迴圈 為解決這個問題, Java 提供了另一種 break/continue 敘述的寫法, 就是在 break/continue 敘述之後加上標籤 (Label) 61 標籤與 break/continue 敘述 在迴圈敘述之前, 可加上標籤來識別迴圈, 其格式如下 : 替迴圈加上標籤後, 在內迴圈中都可在 break/continue 敘述後加上該標籤名稱, 表示要中斷的是指定標籤的迴圈, 例如 : 62 31
標籤與 break/continue 敘述 此時 break 敘述不止會跳出最內層的 do 迴圈, 也會跳出外層的的 for while 迴圈 63 標籤與 break/continue 敘述 我們仍用前面的九九乘法表範例程式來說明, 假設我們現在要讓 1~9 的乘法表中各只列出乘積小於或等於 25 的項目, 此時可在內迴圈中檢查迴圈變數的乘積, 大於 25 就中止該輪外迴圈, 因此程式可改成如下 : 64 32
標籤與 break/continue 敘述 65 標籤與 break/continue 敘述 66 33
標籤與 break/continue 敘述 67 標籤與 break/continue 敘述 第 11 行的 if 敘述判斷 p 是否大於 25, 是就輸出換行字元, 並於第 13 行用 "continue outloop;" 敘述終止這一輪的 outloop 迴圈 ( 第 7 行 ) 所以最後輸出的乘法表中, 沒有乘積超過 25 的項目 68 34
6-6 綜合演練 迴圈與 if 條件式判斷式混合應用 : 取出 1 到指定數值之間的質數 巢狀迴圈的應用 : 利用迴圈輸出等腰三角形圖案 各種迴圈的混合應用 : 計算階乘 69 取出 1 到指定數值之間的質數 在國中的數學課程裡, 我們有時會為了一個數值是不是質數而花時間去計算 因為質數就是除了 1 及其本身之外, 無法被其他整數整除的數, 所以, 如果我們用手動計算的話, 數字越大往往就越難辨別 現在我們可以利用巢狀迴圈來解決這類的問題 原理很簡單, 將數字除以每一個比它小的整數 ( 從 2 開始 ), 只要能被整除的數值就不是質數, 反之則為質數 70 35
取出 1 到指定數值之間的質數 假設 i 是要判斷是否為質數的正整數 ( 且大於等於 2), 則我們可用如下的流程來判斷 i 是不是質數 71 取出 1 到指定數值之間的質數 以上述的流程為基礎, 我們設計一個可計算出 2 到指定數值間的所有質數的程式 此程式使用巢狀迴圈, 外迴圈從 2 到指定數值, 內迴圈則實作上述的流程, 檢查目前的 i 值是否為質數 72 36
取出 1 到指定數值之間的質數 73 取出 1 到指定數值之間的質數 74 37
取出 1 到指定數值之間的質數 75 取出 1 到指定數值之間的質數 76 38
取出 1 到指定數值之間的質數 第 17-32 行的外迴圈, 是用來重複計算 2 到指定數值間的所有整數是否為質數 第 20-24 行為內迴圈, 功能是將目前的 i 值, 依序除以所有小於它的整數 若無法除盡, 則不變動 isprime 的值 ; 若可以除盡, 則將 isprime 設為 false, 表示此數為非質數, 並跳出此迴圈 ( 因為已經確定它不是質數, 就不用再繼續做除法運算 ) 77 取出 1 到指定數值之間的質數 第 26-31 行的程式會先檢查 isprime 的值是否為 true, 是就將目前的 i ( 質數 ) 輸出到螢幕上, 同時將 count 的值加 1 在第 29 行則用 count 除以 5, 檢查目前是否為 5 的倍數, 是就換行, 達到每行只呈現 5 個質數的效果 第 33~34 行是在外迴圈也執行完後, 輸出 count 值, 以顯示在指定的範圍內共有幾個質數 78 39
利用迴圈輸出等腰三角形圖案 在之前的範例中, 我們曾設計一個可以繪製等腰直角三角形的程式, 現在我們更要利用巢狀迴圈來繪製一個等腰三角形 繪出的圖形如下 : 79 利用迴圈輸出等腰三角形圖案 如上所示, 我們用外迴圈來控制換行, 而用內迴圈控制各行的輸出 輸出每一行時, 需先輸出數個空白字元, 再輸出數個 * 符號 瞭解這樣的關係後, 我們可用兩個內迴圈分別輸出空白及 *, 程式如下 : 80 40
利用迴圈輸出等腰三角形圖案 81 利用迴圈輸出等腰三角形圖案 82 41
利用迴圈輸出等腰三角形圖案 83 利用迴圈輸出等腰三角形圖案 第 10 到 13 取得使用者輸入的數字, 決定要畫多高 ( 幾行 ) 的三角形 第 15 到 21 行為外迴圈, 用來控制換行, 根據使用者輸入之數值, 決定要輸出幾行 第 16 17 行為第一個內迴圈, 用來控制每行應輸出的空白字元數 第 18 19 行為第二個內迴圈, 用來控制每行應輸出的 "*" 號數 84 42
計算階乘 在前面的範例中, 應該不難發現一個程式中是可以同時使用各種迴圈的 比方說, 我們要設計一個可以處理階乘問題的程式, 讓使用者可以輸入任意整數, 程式會計算該數字的階乘並詢問是否要繼續輸入數字做計算 階乘的算法就是將數字從 1 開始依續相乘 : 85 計算階乘 換言之, 我們只要用迴圈持續將 1 到 N 的數字相乘, 或反過來從 N 乘到 1 以從 N 乘到 1 為例, 流程如下 : 86 43
計算階乘 依上述流程設計的程式如下 : 87 計算階乘 88 44
計算階乘 89 計算階乘 90 45
計算階乘 第 10 到 25 行為使用 while 迴圈包整個計算階乘的程式, 使程式可重複請使用者輸入新的值來計算 第 15 行用 if 判斷使用者是否輸入 0, 若是即跳出迴圈, 結束程式 91 計算階乘 第 20 行特別用 long 型別來宣告存放階乘值的 fact, 以便程式能計算較大的階乘值 但即使使用了 long 型別, 也只能計算到 20! 的值 第 21 22 行才是真正在計算階乘值的內迴圈, 計算方式如前面的流程圖所示 92 46