第 7 堂課 - 迴圈與迴圈條件及應用
上次更新日期 2018/08/21
前一小節介紹了 switch 與 while 的簡易迴圈,並且使用了相當有趣的亂數功能。不過,其實迴圈有很多的條件需要設定, 此外,也可能需要透過跳出迴圈或者是離開單一迴圈,以及巢狀迴圈等特別的功能!接下來,讓我們來玩一玩這些迴圈的重要功能!
- 7.0: 英打練習與上週作業執行
- 7.1: while 迴圈控制: break, continue, System.exit(0)
- 7.2: 格式化輸出
- 7.3: do...while 迴圈
- 7.4: for 迴圈與應用
- 7.5: 巢狀迴圈
- 7.6: 參考資料
- 7.7: 課後練習
7.0: 英打練習與上週作業執行
資訊科技的技術,如果不是老闆,生產力人員 (就是你我) 通常不會只使用手持式裝置,至少會使用到各式鍵盤。因此,打字就非常重要, 好的打字速度帶你上天堂,差的打字速度讓你呆成糖 (蟲的台語,慢吞吞之意)!關於打字練習的重要注意事項請參考第 0 堂課的說明, 同時,請花 10 分鐘完成第 0 堂課的英文打字練習。完成之後,請打開你的 notepad++ 軟體,輸入底下的字串喔!
public class demo { ↵ public static void main (String args[]) { ↵ int i = 1; ↵ while (true) { ↵ System.out.println ("累加值: " + i++); ↵ if ( i >= 1000 ) { ↵ System.out.println ("次數超過 1000 次,不再進行了"); ↵ break; ↵ } ↵ } ↵ System.out.println ("順利完成迴圈運作囉!"); ↵ } ↵ } ↵
你不需要複製貼上,這單純是要讓你的打字速度變快!所以不要太緊張!練習就對了!沒練習完畢?沒關係,有空繼續練習即可!
- 上週作業的執行
請從 class.vbird.tw 上面下載你上週的作業 java 文字檔,然後先行編譯成功後,等待老師過來查閱是否編譯成功與執行成功! 若執行成功,在你的作業成績會打 v !
7.1: while 迴圈控制: break, continue, System.exit(0)
上一堂課我們知道了 while 的迴圈用法,主要的語法就是『 while (條件) {動作...} 』這樣的格式,因此,最重要的還是在條件的達成上! 但是,如果條件一直無法達成了!?那麼就可能會變成所謂的無窮迴圈了!什麼是無窮迴圈?看一下底下的案例大概就清楚了。
- 分析問題:讓程式一直輸出 1, 2, 3... 一直到無窮大。其實只需要類似累加的條件,就可以直接印出來。
- 解決步驟:
- 宣告類別名稱為 unit07_1_1 (就是檔名)
- 開啟方法為 main 的程式
- 宣告一個名為 i 的整數型態變數,而且初始值給他為 1 即可。
- 建立一個 while 的迴圈,條件則永遠是 true 即可。
- 以類似 System.out.println ("累加值: " + i++ ); 這樣的模式來印出累加的數值即可。
- 關閉 main
- 關閉類別
- 程式設計:主程式的迴圈部份大概是這樣:
int i = 1; while (true) { System.out.println ("累加值: " + i++); }
- 編譯、執行與測試:開始執行『 java unit07_1_1 』之後,程式就會一直輸出數值!如果都不動他,那程式就運作到天荒地老! 或者是運作到整數值的最大值,產生程式溢位之後才結束吧!
當發生程式進入無窮迴圈之後,該程式就無法自動停止。如果產生這樣的情況,你要關閉該程式,只要按下『 [ctrl]+c 』這樣的組合按鍵,就可以將該程式中斷了! 這隻程式還沒有很嚴重的影響到系統,畢竟只是單純的輸出,所以不會太耗費系統資源。如果是某些大型運算程式,產生這種無窮迴圈時, 那整個系統的資源被瘋狂應用的情況下,最終可能導致系統產生高熱而當機....
- 讓整個 JVM 中斷的指令: System.exit(0)
因此,為了避免無窮迴圈的發生,因此,程式設計師可以在迴圈裡面設計一些跳脫的條件~舉例來說,如上所示,當迴圈次數超過 1000 次的時候,就讓他停下來。 除了寫在 while 的條件內部,也能夠在迴圈時增加額外的限制!例如將主程式的迴圈內,增加這一個設計:
int i = 1;
while (true) {
System.out.println ("累加值: " + i++);
if ( i >= 1000 ) {
System.out.println ("次數超過 1000 次,不再進行了");
System.exit(0);
}
}
重新編譯之後再進行『 java unit07_1_1 』的執行,你就會發現當 i 等於 1000 的時候,整隻程式就結束了。但是,如果你想要在迴圈結束後, 再增加一個說明說已經結束了,這個時候的程式就會有點像這樣:
int i = 1;
while (true) {
System.out.println ("累加值: " + i++);
if ( i >= 1000 ) {
System.out.println ("次數超過 1000 次,不再進行了");
System.exit(0);
}
}
System.out.println ("順利完成迴圈運作囉!"); // 在這裡新增說明迴圈已經結束
不過這樣的條件底下進行編譯,卻會出現如下的錯誤:
unit07_1_1.java:11: error: unreachable statement System.out.println ("順利完成迴圈運作囉!"); ^ 1 error
看起來語法都沒有問題,而且出現的錯誤很有趣,中文的解釋是『error: unreachable statement ==> 錯誤,無法到達的狀態!』啥鬼!? 這是因為 System.exit(0) 執行之後,該程式碼會讓整個 JVM 結束,由於整個 JVM 會被中斷掉,任何其他程式碼將無法執行, 因此,在該程式段之後的程式,就不會被執行!因此該段程式就會被告知無法被執行啊~你要想想怎麼辦才行!
- 讓程式只離開迴圈段落的方法: break
所以,在迴圈裡面使用 System.exit(?) 應該是不對的!那該如何是好呢?最簡單的方法,其實就是透過中斷迴圈的 break 來處理比較妥當!所以, 同樣將 unit07_1_1.java 再次修改成底下的模樣:
int i = 1;
while (true) {
System.out.println ("累加值: " + i++);
if ( i >= 1000 ) {
System.out.println ("次數超過 1000 次,不再進行了");
break;
}
}
System.out.println ("順利完成迴圈運作囉!"); // 在這裡新增說明迴圈已經結束
再次執行你就會發現迴圈執行完畢後,還會有額外的資訊告訴你了!現在你就知道 break 的功能了吧!所以未來你的程式越寫越長, 而執行某些迴圈需要加上額外的判斷時,要離開迴圈請務必記得 break 這個程式碼!
- 終極密碼
有個遊戲名稱叫做『終極密碼戰』,其實就是先讓主持人 (系統) 隨機寫一個 1~100 的數字,然後讓所有參與者去猜數字~ 每個人心中要假設有個數字,而且要避過該數字,最後不小心猜中數字的人,就是失敗者~當然,也可以反過來,讓所有使用者去猜數字, 猜到數字的人就是贏了!看大家的規則怎麼寫這樣。那如果我們要用這樣的狀態來撰寫一個可以讓大家猜密碼的程式, 該如何處理呢?
- 分析問題:重點是要讓 (1)系統隨機產生一個數字 (2)讓使用者一直猜數字,直到猜中才離開程式!
- 解決步驟:因為要讓用戶填寫數值,所以當然就得要有互動!然後,使用者會猜幾次不知道,所以當然需要用迴圈來讓用戶一直輸入。此時,就得要加入條件判斷了!
- 使用終端機輸入,因此需要匯入 java.util.Scanner 套件
- 宣告類別名稱為 unit07_1_2 (就是檔名)
- 開啟方法為 main 的程式
- 建立名為 input 的 Scanner 物件
- 先讓系統使用 Math.random() 的方式,隨機產生一個 1~100 間的數字 (參考前一章喔!),假設變數名稱為 syspass 且為整數型態
- 開始要讓使用者猜測密碼,使用者的密碼變數為 mypass,此時得要進入迴圈,且當 mypass != syspass 時,就會一直進行迴圈
- 顯示讓使用者輸入密碼的字眼
- 透過 nextInt() 取得使用者的密碼,並且分配為 mypass
- 判斷 mypass 是否等於 syspass,若比較小,則提示『再增加一些』,若比較大則提示『再減少一些』, 若相等,就顯示『你猜對啦!』,並且離開迴圈
- 透過 System.out.println 顯示遊戲結束
- 關閉 main
- 關閉類別
- 程式設計:主要迴圈部份的設計會像這樣:
while (syspass != mypass) { System.out.print ("請輸入 0 ~ 100 的密碼數值: " ); mypass = input.nextInt(); if ( mypass < syspass ) { System.out.println ("再增加一些"); } else if ( mypass > syspass ) { System.out.println ("再減少一些"); } else { System.out.println ("答對啦!就是這個數字啦!"); break; } }
- 編譯、執行與測試:開始測試執行流程。
C:\Users\dic\java>java unit07_1_2 請輸入 0 ~ 100 的密碼數值: 50 再增加一些 請輸入 0 ~ 100 的密碼數值: 70 再減少一些 請輸入 0 ~ 100 的密碼數值: 60 再減少一些 請輸入 0 ~ 100 的密碼數值: 55 再減少一些 請輸入 0 ~ 100 的密碼數值: 53 再減少一些 請輸入 0 ~ 100 的密碼數值: 51 答對啦!就是這個數字啦! 遊戲結束囉!
如果兩個以上的使用者,輪流去猜測這個密碼,就可以產生很多很好玩的遊戲了!
- 使用 continue 讓程式運作速度加快
某些時刻,你可能會根據在迴圈內的判斷式來處理工作,但有的時候明明第一個工作已經判定不用繼續進行,那麼後續的工作就應該不要進行! 這樣的流程應該不是 break,因為 break 是跳離整個迴圈,而我們目前『只是想要跳出目前這次的迴圈』,『這次與整個』的差別喔! 那就使用『 continue 』吧!基本上, continue 與 break 的流程圖有點像這樣:
上圖左側是 break 流程,右側是 continue 流程。在 break 的流程中,你會發現,break 將跳離整個迴圈,也就是說,如果迴圈後面還有其他的數值, 那就不會繼續運作。而 continue 則是跳脫該次迴圈運作,因此只有後續的『迴圈內任務-...』被略過一次,然後繼續下一個迴圈數值的運作。 一般剛剛開始學程式設計的朋友,可能不會使用 continue 的行為,如果是這樣,那麼原本不用進行的『迴圈內任務-...』就會被持續執行, 此時就會浪費些許資源。若使用 continue,就可以處理掉這個問題。
我們來讓使用者輸入一個數值,例如 100 好了。然後計算 1 到 100 之間的數值,若數值是 7 的倍數或尾數為 7 的時候,就顯示一個小老鼠符號 (@), 否則就將該數值在螢幕上面顯示出來!那該如何進行。這個在團康的時候很長玩!只是團康的時候常用的分母為 3 ,這裡我們用 7 來處理而已。
- 分析問題:倍數為 7 或尾數為 7 可以使用 N % 7 以及 N % 10 來確認餘數為 0 或為 7 即可
- 解決步驟:
- 需要匯入 java.util.Scanner 物件,讓使用者可以輸入數值
- 宣告類別名稱為 unit07_1_3 (就是檔名)
- 開啟方法為 main 的程式
- 讓使用者輸入一個數值,並說明最好能夠大於 50 較佳。
- 開始進入迴圈,設定一個 i 整數型態數值,且透過 i++ 來每次累加 1,直到 i 大於使用者輸入值就結束
- 判斷 i 是否能被 7 整除,或者是被 10 整除餘數為 7,若是,則 (1)輸出 @ 且 (2)透過 continue 進到下個數值
- 印出此數值
- 透過 System.out.println 說明遊戲結束
- 關閉 main
- 關閉類別
- 程式設計:
int i = 0; while ( i <= usernu ){ i++; if ( i%7 == 0 || i%10 == 7) { System.out.print ("@, "); continue; } System.out.print (i + ", "); } System.out.println ("遊戲結束囉!");
- 編譯、執行與測試:開始測試執行流程。
C:\Users\dic\java>java unit07_1_3 請輸入一個數值,最好大於 50 較佳。我會取消 7 的倍數或尾數為 7 的數值: 50 1, 2, 3, 4, 5, 6, @, 8, 9, 10, 11, 12, 13, @, 15, 16, @, 18, 19, 20, @, 22, 23, 24, 25, 26, @, @, 29, 30, 31, 32, 33, 34, @, 36, @, 38, 39, 40, 41, @, 43, 44, 4 5, 46, @, 48, @, 50, 51, 遊戲結束囉!
上面的練習當然是比較無俚頭的~基本上,使用 if ... else 也能達到上述的作法!只是這邊提供比較多的思考!
C:\Users\dic\java>java unit07_1_2_new 請輸入 1 ~ 100 的密碼數值: 50 請輸入 51 ~ 100 的密碼數值: 70 請輸入 71 ~ 100 的密碼數值: 90 請輸入 71 ~ 89 的密碼數值: 80 請輸入 81 ~ 89 的密碼數值: 85 請輸入 81 ~ 84 的密碼數值: 83 請輸入 81 ~ 82 的密碼數值: 82 答對啦!就是這個數字啦! 遊戲結束囉!
7.2: 格式化輸出
回到剛剛上一小節的最後一個課堂練習,我們發現到整個遊戲輸出的資訊有點難看,就是沒有排列整齊!有點醜醜的。如果需要排列整齊的時候, 就需要所謂的『格式化輸出』 (format) 的動作了。一般來說,所謂的格式化輸出,就是透過一些我們所制定的格式,來讓整個輸出訊息排列較整齊的情況。 而我們常見的需要的格式化輸出有『字串數』、『整數長度』、『浮點數長度』等而已,其他的就先不予理會,底下的表格就是常見的格式了!
格式撰寫法 | 輸出資料 | 範例 |
%d | 十進位的整數值 | %4d --> " 200" (前面補空白) |
%f | 具有小數的浮點數 | %5.2f --> " 3.14" (共 5 個數值,小數點下 2 個位數) |
%s | 字串長度 | %10s --> " I'm VBird" (前面補空白) |
至於處理的方案中,以『請輸入 1 ~ 100 的密碼數值: 』這樣的格式來說,基本上原始的程式碼是這樣設計的:
System.out.print ("請輸入 " + minpass + " ~ " + maxpass + " 的密碼數值: " );
用 + 號連結 5 個資料,而資料有字串、整數、字串、整數、字串這樣。那當我們要讓兩個整數使用 3 個位數來處理時,整個程式碼就應該要改成這個模樣:
System.out.printf ("%s%3d%s%3d%s", "請輸入 ", minpass, " ~ ", maxpass, " 的密碼數值: " );
也就是說,用 System.out.printf 的那個 printf 來取代原本的 print 或 println,然後整個括號內的第一個項目先設定格式, 之後看有幾個百分比符號 (%) 就需要外接幾個參數的意思。請將 unit07_1_2_new.java 另存新檔成為 unit07_2_1.java,並且修改上述的程式段, 重新編譯並執行 『java unit07_2_1』,就會出現類似底下的運作流程:
請輸入 1 ~ 100 的密碼數值: 50 請輸入 1 ~ 49 的密碼數值: 25 請輸入 26 ~ 49 的密碼數值: 37 請輸入 38 ~ 49 的密碼數值: 45 請輸入 46 ~ 49 的密碼數值: 48 答對啦!就是這個數字啦! 遊戲結束囉!
你會發現到整個輸出的訊息要漂亮的多了!這就是格式化輸出的優點了!那如果需要浮點數呢?舉例來說,整個浮點數的目的在算出 pi 的話, 我們知道 pi 的值應該是 3.14159... 這樣的數值,假設我要讓程式輸出 10 個數值,且整數為 2 位數,小數點以下則具有 8 位數,因此就會使用:『 %10.8f 』這樣的格式! 那個 10.8 的意思是,共有 10 個數字,其中 8 個數字是小數點以下位數的意思。
讓我們回到第二章的 2.7 小節,裡面有談到需要使用程式來計算 pi 值,簡單的計算式是:
pi = 4 x (1/1-1/3+1/5-1/7+1/9-1/11+1/13) = 計算的結果
當時我們只用到 13 ,因為還不會使用迴圈的緣故。現在,讓我們使用迴圈來計算這個 pi 值~因為分子都是 1 所以就不理他,至於分母呢,第一個是 2*1-1,第二個是 2*2-1,第三個是 2*3-1,第四個 2*4-1,所以總結在迴圈裡面就會變成『 2*i-1 』這樣的模式。然後還得要注意,i 是奇數時,要乘上 1,而 i 是偶數時,就得乘上 -1 才行。 所以,如果 pi 是在迴圈裡面的話,那麼上述計算式小括號裡面的資料就會變成
double i = 0; double cross = 1; double pi1 = 0; while ( i <= 1000 ) { i++; if ( i % 2 == 1 ) { cross = 1; } else { cross = -1; } pi1 = pi1 + 1.0/(2.0*i-1)*cross; } double pi = 4 * pi1;
那最後我要輸出的訊息是『 3.14159... 』共 10 個數字時,該如何撰寫最後面的輸出碼?請自行處理,最終輸出的結果會有點像這樣:
C:\Users\dic\java>java unit07_2_2
PI = 3.14159365
C:\Users\dic\java>java unit07_2_3 你要計算 pi 到幾位數 (ex> 6): 6 PI = 3.141594 C:\Users\dic\java>java unit07_2_3 你要計算 pi 到幾位數 (ex> 6): 7 PI = 3.1415928
上面的練習題確實還挺有趣的!大家可以測試一下!同時理解一下格式化輸出與迴圈呢!不過,不要輸入超過 8 (最大就到 8 吧),否則程式會算很久!
7.3: do...while 迴圈
除了單純的 while 迴圈之外,還有另外一種有趣的也跟 while 有關的迴圈,就是 do 的迴圈。他的語法是這樣的:
do { 迴圈內的動作; ... } while (條件判斷);
你會發現 while 變成在區塊 ({}) 的後面了!取而代之的是 do 這個關鍵字。其實 do 與 while 迴圈幾乎是一模一樣! 差別在於『 do 的迴圈一定會進行過一次,然後再帶入 while 進行判斷 』的情況!如果單純是 while 的話,那麼會先進行條件判斷, 如果條件判斷沒有成功,則該迴圈就不會進行!這樣理解差異嘛?
請將 unit07_1_3.java 另存新檔為 unit07_3_1.java,請將 7 改成 3 之外,並且使用 do 迴圈來取代 while 迴圈。基本上,迴圈的主程式大概會變成這樣:
int i = 0; do { i++; if ( i%3 == 0 || i%10 == 3) { System.out.print ("@, "); continue; } System.out.print (i + ", "); } while ( i <= usernu );
就是位置的變化而已。基本上,你只要記得 while 應該就很夠用了!
7.4: for 迴圈與應用
只要是 while 方面的迴圈,如果涉及到固定的數值變化,就得要加上控制碼才行。同時, while 迴圈也能夠進行不同的條件判斷。那如果如同上面的許多練習, 大部分的迴圈就是數值的變化而已呢?我們如果想要設計固定數值的迴圈次數,似乎使用底下的 for 迴圈,會比 while 迴圈更方便設計也方便閱讀。
for (初始條件; 運算條件; 增值) { 迴圈內的動作; ... }
現在,讓我們將 unit07_3_1.java 另存新檔為 unit07_4_1.java,然後將 do 迴圈以 for 迴圈來處理,主程式的部份會有點像這樣:
for (i=1; i<=usernu; i++) {
if ( i%3 == 0 || i%10 == 3) {
System.out.print ("@, ");
continue;
}
System.out.print (i + ", ");
}
你不用在程式碼裡面使用 i++ 做判斷,直接在 for 迴圈裡面進行增值的設計即可!至於判斷式則是兩個分號中間,也就是『 i <= usernu 』那一個設計! 這樣的程式碼看起來當然舒服很多!
- 計算累加值
有時候我們需要計算累計值,例如 1+2+3+...+1000,假如不是透過快速計算式,而是直接透過真的加總來計算的話,那該如何使用 for 迴圈來處理?
- 分析問題:其實很簡單,就是類似 sum = sum + i,而 i 會持續長大這樣,最終印出 sum 就結束了
- 解決步驟:
- 匯入 java.util.Scanner,讓使用者輸入想要累加的數字
- 宣告類別名稱為 unit07_4_2 (就是檔名)
- 開啟方法為 main 的程式
- 使用 for 迴圈,讓 i 初始值為 1 ,且運算條件為 i < 使用者輸入值,然後進入迴圈
- 以 sum = sum + i 來累加數值囉
- 透過 System.out.println 來印出最終的結果
- 關閉 main
- 關閉類別
- 程式設計:
int i = 0, sum = 0; for (i=1; i<=usernu; i++) { sum = sum + i; } System.out.println ("1+2+..+" + usernu + " = " + sum);
- 編譯、執行與測試:開始測試執行流程。
C:\Users\dic\java>java unit07_4_2 請輸入一個整數,我會對他做累加: 101 1+2+..+101 = 5151
C:\Users\dic\java>java unit07_4_3 請輸入一個整數,我會對他做乘暮: 10 1x2x3..x10 = 3628800 C:\Users\dic\java>java unit07_4_3 請輸入一個整數,我會對他做乘暮: 50 1x2x3..x50 = 0 <==為什麼會這樣? C:\Users\dic\java>java unit07_4_3 請輸入一個整數,我會對他做乘暮: 30 1x2x3..x30 = 1409286144
上面的練習當中,思考一下,為什麼輸入 50 之後,系統會出現計算結果為 0 呢!在未來設計數值程式的時候,這點是相當重要的概念喔!
- 持續計算 pi 值
除了 unit07_2_3.java 可以計算 pi 值之外,事實上還有另外一個可以收斂的 pi 值計算會比我們之前的計算值還要快速,如下圖所示,就是另外的一個計算式了:
- 分析問題:仔細分析一下上面的算式,我們會發現到分母是以 2 為一個單位去累加,然後,累加次數為奇數時,則乘上 +1,若為偶數則乘上 -1 這樣的情況。
- 解決步驟:
- 匯入 java.util.Scanner 這個套件,讓用戶來輸入一個位數
- 宣告類別名稱為 unit07_4_4 (就是檔名)
- 開啟方法為 main 的程式
- 設定一個名為 input 的 Scanner 物物件。
- 顯示一個『請輸入一個位數』的單純顯示,然後讓使用者透過 input.nextDouble() 來取得一個位數值
- 給予 i, cross, pi 的宣告與初始值,其中 pi 初始值請設定為 3 (請看上面的算式)
- 開始進入迴圈,以 i 為變數,且起始值為 2 ,當 i <= Math.pow(10,位數值) 就持續運算,步階為 i=i+2,開始迴圈
- 處理 cross 的數值,當 (int)(i/2)%2 == 1 成立,則 cross 為 1,否則則為 -1
- 計算 pi = pi + 4/(i * (i+1) * (i+2)) * cross 這樣的數值 (就是上方的 pi 計算)
- 透過 System.out.println 將 pi 的值輸出
- 關閉 main
- 關閉類別
- 程式設計:主要的 for 迴圈大致上有點像這樣:
for (i=2; i<=Math.pow(10,usernu); i=i+2) { if ( (int)(i/2) % 2 == 1 ) cross = 1; else cross = -1; pi = pi + 4 / (i * (i+1) * (i+2)) * cross; }
- 編譯、執行與測試:開始測試執行流程。
C:\Users\dic\java>java unit07_4_4 請輸入一個整數位數,我會計算 pi 的次數 (ex> 7): 3 pi = 3.141592651601756 C:\Users\dic\java>java unit07_4_4 請輸入一個整數位數,我會計算 pi 的次數 (ex> 7): 5 pi = 3.141592653589785 C:\Users\dic\java>java unit07_4_4 請輸入一個整數位數,我會計算 pi 的次數 (ex> 7): 7 pi = 3.141592653589787
我們可以很輕鬆的就得到很正確的數值!帶入 3 次方就已經得到小數點下 7 位數的正確值了!相當快速!
7.5: 巢狀迴圈
過去我們曾經算過九九乘法表的單一數值,就是在 unit04_2_1.java 程式碼裡面設計的,讓使用者輸入一個 (2~9) 的值,然後我們將該值帶入 1~9 的乘積, 就得到該數值的乘法表。好笨~現在可以使用到迴圈了,當然很方便的就能夠計算出九九乘法表!只是,因為有兩個變數,因此,我們需要寫兩層迴圈, 假設九九乘法表是 X*Y=Z 的話, X 是一層迴圈,Y 則是包含在 X 內的另一層迴圈。由於有兩層包在一起的迴圈,因此就成為巢狀迴圈。若以 for 迴圈為例, 語法就會有點像這樣:
for (x=1; x<=9; x++) { for (y=1; y<=9; y++ ) { System.out.println (x + " x " + y + " = " + x*y ); } }
- 繪製 9x9 乘法表 (使用兩種格式)
上面的 unit07_5_1.java 執行成果有夠爛,竟然是一條長串輸出,這樣實在很難查閱乘法表的結果啊!假如我們需要的是如下的輸出狀態,那應該如何撰寫呢?
1x1= 1 2x1= 2 3x1= 3 4x1= 4 5x1= 5 6x1= 6 7x1= 7 8x1= 8 9x1= 9 1x2= 2 2x2= 4 3x2= 6 4x2= 8 5x2=10 6x2=12 7x2=14 8x2=16 9x2=18 .... 1x9= 9 2x9=18 3x9=27 4x9=36 5x9=45 6x9=54 7x9=63 8x9=72 9x9=81
- 分析問題:如果要達成上述的輸出,我們得要知道『終端機的輸出,主要是從左而右,從上而下』這樣的格式! 所以,從上面的分析來看,第一橫列變化的是 XxY,就是那個 X 會變化!因此,若以之前 unit07_5_1.java 拿來修改的話, X 與 Y 的結構需要調整, 讓 Y 在外層而 X 在內層才可以。此外,不能用 System.out.println 了,得要使用 System.out.printf 搭配格式來處理才行!
- 解決步驟:
- 宣告類別名稱為 unit07_5_2 (就是檔名)
- 開啟方法為 main 的程式
- 進入第一層 for 迴圈, y 的設計
- 進入第二層 for 迴圈, x 的設計
- 開始處理輸出,包括資料輸出與乘法輸出
- 記得要斷行
- 進入第二層 for 迴圈, x 的設計
- 關閉 main
- 關閉類別
- 程式設計:兩層迴圈其實很簡單,但是格式化輸出比較需要注意!會有點像這樣:
System.out.printf ("%1d%1s%1d%1s%2d%2s", x, "x", y, "=", x*y,"" );
- 編譯、執行與測試:開始測試執行流程。
上面是將所有的算式都列出來,如果想要簡化整個畫面的話,還有底下這種也算很常見的輸出組合:
| 1 2 3 4 5 6 7 8 9 <==這一行 Y=0,結果輸出的是 X 的值 ---------------------------------------- <==在 y=1 時,額外增加一個減號的輸出迴圈! 1| 1 2 3 4 5 6 7 8 9 <==最左邊數值為 Y,右邊 9 個值為 XxY 2| 2 4 6 8 10 12 14 16 18 <==同上,就一直處理 9 次就對了! 3| 3 6 9 12 15 18 21 24 27 4| 4 8 12 16 20 24 28 32 36 5| 5 10 15 20 25 30 35 40 45 6| 6 12 18 24 30 36 42 48 54 7| 7 14 21 28 35 42 49 56 63 8| 8 16 24 32 40 48 56 64 72 9| 9 18 27 36 45 54 63 72 81
如果想要達成上述的輸出,可能還得要加上許多技術。同樣的,我們以 XxY 的角度來思考,當 X 為 0 的時候,就得要輸出 Y 的值,然後非為 0 的時候, 才是輸出 XxY 的值。至於當 Y 為 0 的時候,整個輸出的值則是 X 喔!所以整個迴圈可能會變成底下的模樣:
for (y=0; y<=9; y++) { if ( y == 1 ) { // 第二行輸出減號,美化整個輸出畫面而已 for ( z=1; z<=40;z++ ) { System.out.print ("-"); } System.out.print ("\n"); } for (x=0; x<=9; x++ ) { // 這裡才是每一行的數值輸出 if ( y == 0 && x == 0 ) { // 第一行的第一個輸出 System.out.printf ("%3s%1s", "","|"); } else if ( y == 0 && x != 0 ) { // 第一行的後面 9 個輸出 System.out.printf ("%4d", x); } else if ( y != 0 && x == 0 ) { // 第三行以後的第一個輸出 System.out.printf ("%3d%1s", y,"|"); } else { // 第三行以後的 9 個輸出 System.out.printf ("%4d", x*y); } } System.out.print ("\n"); }
上述的資料如果會自己分析又看得懂,那就太棒啦!
- 用 * 繪製聖誕樹
還記得以前剛剛碰觸程式時,程式能夠在終端機輸出一個聖誕樹,實在是讓人很開心啊!啥!?聖誕樹!對的,就如下所示, 醜醜一顆感覺像是樹的東西而已。不過,你能夠使用程式繪製出這樣的圖示嘛?
* *** ***** ******* ********* *********** *** *** *** (看最大你可以作到多大!)
不過,在完成這個東西之前,我們先來測試比較簡單的東西。假設你要讓使用者輸入 X 與 Y 軸,然後透過以雙層迴圈的行為, 輸出一個矩形的星號圖示,該如何處理?
- 分析問題:就如同前面說的:『終端機的輸出是由左而右,由上而下』的情形,因此,會先輸出 X 軸,再由 Y 軸持續輸出 X 。
- 解決步驟:
- 因為要讓使用者輸入資料,所以匯入了 java.util.Scanner 物件
- 宣告類別名稱為 unit07_5_4 (就是檔名)
- 開啟方法為 main 的程式
- 讓使用者輸入兩個整數型態的變數,分別設定為 xx 與 yy 好了。
- 開始 進入 for 的迴圈,以 y 的初始值 1 運算條件在 y <= yy 且步階為 1 的情況下
- 開始進入 for 的迴圈,子 x 的初始值 1,運算條件 <== xx 且步階為 1
- 每次只輸出一個 * ,且不會斷行
- 執行一個斷行功能
- 開始進入 for 的迴圈,子 x 的初始值 1,運算條件 <== xx 且步階為 1
- 關閉 main
- 關閉類別
- 程式設計:太簡單了!所以請自己寫
- 編譯、執行與測試:開始測試執行流程。
C:\Users\dic\java>java unit07_5_4 請以空白隔開,分別輸入 X 與 Y 需要的 * 個數: 10 5 ********** ********** ********** ********** **********
C:\Users\dic\java>java unit07_5_5 請輸入一個邊長,我來設計出直角三角形: 10 * ** *** **** ***** ****** ******* ******** ********* **********課後有一題要畫出倒三角形的,請仔細玩一玩!
現在讓我們來完成簡單聖誕樹吧!由聖誕樹的個數來思考,第一行 1 個,第二行 3 個,第三行 5 個,因此個數會是 2n-1 個,n 代表行數。 而總共有六行,所以最大有 2*6-1=11 個星星,所以中心點應該是在第 6 個位置上。而我們從『終端機的輸出是由左而右、由上而下』的型態來看, 所以結果是:
- 第一行先輸出 5 個空白,然後輸出 1 個 *;
- 第二行先輸出 4 個空白,然後輸出 3 個 *;
- 第三行先輸出 3 個空白,然後輸出 5 個 *;
- ....
- 第六行先輸出『 6-行數 』個空白,然後輸出『 2x行數-1 』 個 *;
這樣就簡單了!我們就可以設計 unit07_5_6.java 的前半部,也就是樹葉的部份,大致上的程式碼會有點像這樣:
for (i=1;i<=6;i++ ) { // 這裡說的是行數 for ( j=1; j<=6-i; j++ ) { // 這裡則是空白的處理 System.out.print(" "); } for ( k=1; k<=2*i-1; k++ ){ // 這裡是星號的處理 System.out.print ("*"); } System.out.println(""); }
接著下來則是樹幹的部份,樹幹主要給 3 個星星,這是固定的!然後,當然要是中心點 (6) 再少一個點,然後給予 3 個星星即可! 也就是說,空白的部份會到 6-2 的意思。然後我們給個 5 個高度好了!所以程式碼會有點像這樣:
for (i=1;i<=5;i++){ // 意思是有 5 行的樹幹星星 for (j=1;j<=6-2;j++){ // 中心點往左 2 格是空白 System.out.print(" "); } System.out.println("***"); // 印出三顆星星 }
最後將兩個資料組合在一起,執行後的結果就會變成這樣了:
C:\Users\dic\java>java unit07_5_6
*
***
*****
*******
*********
***********
***
***
***
***
***
課後有一題要處理讓使用者輸入樹葉的行數數量來畫圖的,請仔細玩一玩!
- 計算某範圍內的質數有幾個
程式還可以拿來幫我們計算質數。所謂的『質數』指的是在整數的範圍內,只有 1 與本身才能整除。wiki 的說明是 『指在大於1的自然數中,除了1和該數自身外,無法被其他自然數整除的數』。那麼我們怎麼計算質數呢?可以使用所謂的『試除法』, 試除法的定義是:『給定一個合數n(這裡,n是待分解的正整數),試除法看成是用小於等於 n0.5 的每個質數去試除待分解的整數。如果找到一個數能夠整除除盡,這個數就是待分解整數的因子。
最簡單的說明就是,假設你要知道 n 是不是質數,那就是用 1, 2, 3, 4... 一直到 n0.5 去除 n, 若可以整除,那就代表不是質數,如果無法整除,那就代表 n 是質數。那你能不能知道 1~1000 之內有多少質數呢? 這就使用計算的方法來處理了。
- 分析問題:要找出一個範圍內的質數,一定要從 2 開始,因為 1 一定能整除啊!假設數值到 11 的時候,那麼 110.5 是 3.32,因此取 3 來進行除法, 11/2, 11/3 都無法整除,因此 11 就是質數。
- 解決步驟:
- 匯入 java.util.Scanner 套件
- 宣告類別名稱為 unit07_5_7 (就是檔名)
- 開啟方法為 main 的程式
- 建立名為 input 的物件
- 讓使用者輸入一個數值來進行質數的計算,假設為 xx 這個整式型態的變數名稱
- 進入 for 迴圈,x 初始值為 2,運算值 x <== xx,步階為 1
- 設定 vbyes 為 1 ,亦即假設預設為質數之意
- 計算最大除數,亦即 (int)Math.pow(x,0.5) 這個數值,設定為 yy 這個整數型態變數
- 當 yy 大於等於 2 的時候,才開始後續動作
- 開始 for 迴圈, y 初始值為 2,運算值為 yy,步階為 1
- 如果 x 可以被 y 整除,就設定 vbyes = 0,且立刻 break 掉迴圈
- 開始 for 迴圈, y 初始值為 2,運算值為 yy,步階為 1
- 判斷 vbyes 是否為 1,若為 1 就顯示 x 的數值,否則就略過,繼續下個計算。
- 關閉 main
- 關閉類別
- 程式設計:主要的迴圈部份設計如下
for ( x = 2; x <= xx; x++ ) { vbyes = 1; yy = (int)Math.pow(x,0.5); if ( yy >= 2 ) { for ( y = 2; y <= yy; y++ ) { if ( x % y == 0 ) { vbyes = 0; break; } } } if ( vbyes == 1 ) System.out.print ( x + ", "); }
- 編譯、執行與測試:開始測試執行流程。
C:\Users\dic\java>java unit07_5_7 一個數值範圍,我將計算該範圍內的質數: 100 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97,
基本上這個試除法就有很多可用的概念~包括那個 break 的功能可以跳脫內層迴圈~是相當有用的程式設計概念!
- 計算最大公約數
我們說, 24 與 18 的最大公約數是 6,因為 24 = 6*4 而 18 = 6*3,所以公約數有 2, 3, 6,最大那個當然就是 6 囉。 那怎麼從程式裡面找出公約數呢?首先,我們來找一下因數好了!讓使用者輸入一個數,然後我們來找出每一個因數。那麼什麼是因數呢?很簡單, 就是可以將被除數整除的除數,就是囉!我們也知道,除數是從 N/2 開始往下到 2 來計算,假設你輸入的數值為 24 好了,那麼因數當然是從 12 (24/2) 開始求, 因為大於 12 的數值,將無法整除被除數!所以,現在讓我們來找出使用者輸入的數值的因數:
for ( i = 2; i <= n/2; i++ ) { if ( n % i == 0 ) System.out.println (i); }
那如果要反向輸出呢?就將第一行改成『 for (i=n/2; i>=2; i--) 』即可!現在,讓使用者輸入兩個數,第一個數由大到小找出所有的因數,然後, 再用因數去除以第二個數,若第二個數可以被整除,那就是最大公約數了!最重要的是迴圈加上選擇而已!記得要透過 break 離開迴圈!
C:\Users\dic\java>java unit_07_5_9 用空白隔開,輸入 2 個數值,我來找出最大公約數: 24 18 最大公約數為: 6
7.6: 參考資料
7.7: 課後練習
請先查閱 1.3 小節的介紹,了解雲端系統的登入、繳交資料檔名等等的設計,然後再繼續底下的習題。 最終要上傳的檔案有:
- unit07-4070CXXX-你的名字.docx
- unit07_toone.java (純文字檔)
- unit07_fact.java (純文字檔)
- unit07_triangle.java (純文字檔)
- unit07_tree.java (純文字檔)
- 完成上課的習題內容回答:
- 在迴圈的中斷使用中,System.exit(0) 及 break 的差別在哪裡?
- while () {...} 迴圈與 do {..} while (); 迴圈的差別在哪裡?
- 如何產生一個 1~100 之間的亂數密碼 (整數型態)?
- 我需要輸出 3.141592 這樣的數值格式,使用 System.out.printf 內的格式要求應該怎麼寫?
- 為什麼在 int 的數值型態下,使用 50! 會出現錯誤?該如何訂正這個問題?
- 一般來說,終端機的資料輸出方向為何?
- 讓使用者輸入一個整數,然後循環進行:
- 當數值為偶數,就除以 2
- 當數值為奇數,就給予『 *3+1 』
C:\Users\dic\java>java unit07_toone 輸入一個數值,最後這個數值經過計算會變 1 : 10 1: 5 2: 16 3: 8 4: 4 5: 2 6: 1
- 寫一隻程式,執行程式後,他會算出 1/1!+1/2!+1/3!+...+1/n! 的結果,其中 n 為使用者輸入的資料。當執行『 java unit07_fact 』時,會出現如下的執行成果:
C:\Users\dic\java>java unit07_fact 輸入一個數值,我來計算 1/1!+1/2!+1/3!+...+1/n!: 10 1.7182818011463847
- 模仿 unit07_5_5.java 的程式執行結果,只是輸出的資料為倒三角形,如下所示:
C:\Users\dic\java>java unit07_triangle 請輸入一個邊長,我來設計出直角三角形: 10 ********** ********* ******** ******* ****** ***** **** *** ** *
- 模仿 unit07_5_6.java 的程式執行結果,只是不要使用 6 行,而是讓使用者自己選擇行數,但是行數必須大於等於 6 且小於等於 30 ,
若小於 6 就以 6 執行,若超過 30 就以 30 執行。當執行『 java unit07_tree 』時,會有點像底下的模樣:
C:\Users\dic\java>java unit07_tree 請輸入聖誕樹的大小 (6~30): 15 * *** ***** ******* ********* *********** ************* *************** ***************** ******************* ********************* *********************** ************************* *************************** ***************************** *** *** *** *** ***