迴圈敘述 5
我們 類的生活有許多事都是具有重覆性的, 例如㆒ 有 24 小時, ㆒星期有七, 同㆒門課要 18 次才能拿到學分, 程式設計是要解決日常生活可預期的事件, 為了解決日常生活的重覆性, BCB 的迴圈敘述如 1. for 2. while 3. goto for 的使用時機為設計階段已知執行次數, 請看 5-1 節 若未知執行次數, 則應使用 while, 請看 5-2 節 goto 則為非結構化語言的遺留產物, 原則 它應已走入歷史, 很多 建議應將 goto 敘述從結構化語言移除, 但又擔心有些程式無法適應, 所以目前 goto 敘述還是繼續存在, 請看 5-3 節 但是, 筆者在此建議讀者, 儘量少用 goto, 會有 goto 是因為早期的程式語言沒有迴圈敘述, 所有的迴圈敘述均要配合 if 敘述, 所以寫出來的程式就像㆒團通心麵㆒樣, 後來的任何 ( 含原始設計者 ) 都沒有辦法維護, 為了解決這種困境, 所以有結構化語言的出現, 結構化語言就是要求設計者充份使用適當的迴圈敘述並且少用 goto 5-1 for 如 : 若於程式設計階段已知執行次數, 則可使用 for 敘述, for 敘述的語法 for ([ 計數變數 = 起始值 ];[ 迴圈運算式 ];[ 計數變量 ]) [ 敘述區塊 1;] [break;] [continue;] [ 敘述區塊 2;] 5-2
迴圈敘述 5 以 語法說明如 : 1. 只要 迴圈運算式 結果為真, 則繼續執行迴圈內的敘述區塊 2. 計數變數可為正或負的整數或實數, 正整數請看範例 5-1a, 負實數請看範例 5-1b 3. 程式若執行到 break, 則會提早離開 for 迴圈, 如 所示 for (;;) 敘述區塊 1; break; 敘述區塊 2; 4. 程式若執行到 continue, 則會略過 continue 面的敘述區塊 2, 繼續執行 ㆒個計數變量, 如 所示, 請看範例 5-1c for (;;) 敘述區塊 1; continue; 敘述區塊 2; 5. 以 程式片斷可統計 1 至 10 的和 sum=0; for (i=1;i< = 10;i++) sum=sum+i; 6. 敘述區塊內可以放置任何合法的敘述, 當然也含 for, for 內有 for 稱為 巢狀迴圈, 請看範例 5-1d 5-3
範例 5-1a 請寫㆒個程式, 印出 1 至 10, 並求其和 輸出結果 如右圖 程式列印 ------- int i, sum=0; const fontsize=12 ; // 設定常數 const wordsize=fontsize+4; Canvas->Font->Size=fontsize; for (i=1 ;i<=10;i++) // i++ 同 i=i+1 Canvas->TextOut(0, wordsize*i, i); sum+=i; //sum=sum+i Canvas->TextOut(0, wordsize*i, " 其和為 "+IntToStr(sum)); ------- 5-4
迴圈敘述 5 void fastcall Tfrm::btnendClick(TObject *Sender) Close(); ------- 程式說明 1. BCB 並不會自動將所宣告變數的初值設為 0, 所以若要累加的變數, 務必自己親自設定其初值 2. i++ 的效果同 i=i+1, 但其寫法較簡捷 3. for (i=1;i < =10;i++) 表示只要 i < = 10 就會執行迴圈的敘述區塊, 若寫成 for (i=1;i=10;i++) 則迴圈運算式僅 i=10 結果為 true, i=1 至 9 均得到 false, 不會執行迴圈內的敘述區塊, 此為 BCB 與㆒般語言最大的差異, 請讀者務必小心 範例 5-1b 試求 2.1+1.9+1.7+ +(-7.1) 之和 輸出結果 5-5
程式列印 -- float i, sum=0; for (i=2.1 ;i>=-7.1;i-=0.2) // i-=0.2 同 i=i-0.2 sum+=i; //sum=sum+i Canvas->TextOut(0, 20, " 其和為 " +FloatToStr(sum)); -- void fastcall Tfrm::btnendClick(TObject *Sender) Close(); -- 程式說明 本例之初值為 2.1, 迴圈運算式為大於等於 -7.1, 變量為 -0.2 範例 5-1c 示範 break 及 continue 敘述 輸出結果 1. 當 i==5 時, 略過不印, 如右圖 2. 當 i==8 時, 程式結束, 如右圖 5-6
迴圈敘述 5 程式列印 int i, sum=0; const fontsize=12 ; // 設定常數 const wordsize=fontsize+4; Canvas->Font->Size=fontsize; for (i=1 ;i<=10;i++) // i++ 同 i=i+1 if(i==5) continue; // 繼續 ㆒個 i if (i==8) break; // 強制離開迴圈 Canvas->TextOut(0, wordsize*i, i); sum+=i; //sum=sum+i Canvas->TextOut(0, wordsize*i, " 其和為 " +IntToStr(sum)); void fastcall Tfrm::btnendClick(TObject *Sender) Close(); 範例 5-1d 試寫㆒程式, 計算並印出 1 至 100 的和 10 次 5-7
輸出結果 程式列印 -- int i, j, sum=0; const fontsize=12 ; // 設定常數 const wordsize=fontsize+4; Canvas->Font->Size=fontsize; Canvas->Brush->Style=bsClear; // 設定背景為透明 for (i=1 ;i<=10;i++) // i++ 同 i=i+1 sum=0; for (j=1;j<=100;j++) sum+=j; //sum=sum+j Canvas->TextOut(0, wordsize*i, " 第 " + IntToStr(i) + " 次, 其和為 " +IntToStr(sum)); -- void fastcall Tfrm::btnendClick(TObject *Sender) Close(); -- 5-8
迴圈敘述 5 範例 5-1e 請寫㆒程式輸出如 : * * * * * * * * * * * * * * * * * * * * * 程式列印 -- int i, j; const fontsize=12 ; // 設定常數 const wordsize=fontsize+4; Canvas->Font->Size=fontsize; Canvas->Brush->Style=bsClear; for (i=1 ;i<=6;i++) // i++ 同 i=i+1 for (j=1;j<=i;j++) Canvas->TextOut(wordsize*j, wordsize*i, "*"); -- void fastcall Tfrm::btnendClick(TObject *Sender) Close(); -- 自我練習 請寫㆒程式, 輸出結果如 : 5-9
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 範例 5-1f 請寫㆒程式, 輸出結果如 1 2 1 3 2 1 4 3 2 1 5 4 3 2 1 6 5 4 3 2 1 程式列印 ---- int i, j; const fontsize=12 ; // 設定常數 const wordsize=fontsize+4; Canvas->Font->Size=fontsize; Canvas->Brush->Style=bsClear; for (i=1 ;i<=6;i++) // i++ 同 i=i+1 for (j=1;j<=i;j++) Canvas->TextOut(wordsize*(6-j), wordsize*i, j); ---- void fastcall Tfrm::btnendClick(TObject *Sender) Close(); ---- 5-10
迴圈敘述 5 自我練習 請寫㆒程式, 輸出結果如 : Æ Æ 1 2 2 3 3 3 4 4 4 4 5 5 5 5 5 A B B C C C D D D D E E E E E Æ Æ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 E E E E E D D D D C C C B B A 範例 5-1g 試寫㆒程式, 找出㆔位數的 阿姆斯壯數 所謂阿姆斯壯數是指㆒數等於各位數的立方和, 例如 153= 1 3 + 5 3 + 3 3 輸出結果 5-11
程式列印 -- int i, j, k, n=0; int sum1, sum2; const fontsize=12 ; // 設定常數 const wordsize=fontsize+4; Canvas->Font->Size=fontsize; Canvas->Brush->Style=bsClear; for (i=1 ;i<=9;i++) // i++ 同 i=i+1 for (j=0;j<=9;j++) for (k=0;k<=9;k++) sum1=100*i+10*j+k; // 需 #include <math.h> sum2=pow(i, 3)+pow(j, 3)+pow(k, 3); if (sum1==sum2) n++; Canvas->TextOut(0, wordsize*n, sum1); -- 5-2 while ㆒節的 for 是用於已知迴圈次數, 但有些情況, 我們並不知迴圈的執行次數, 此時即可使用 while 敘述, 且有些迴圈可能㆒次都不執行, 所以 while 敘述又分為前測試迴圈與後測試迴圈, while 的前測試迴圈語法如 圖左, 後測試迴圈如 圖右 5-12
迴圈敘述 5 while (¹Bºâ ) ±Ô z Ï ô; do ±Ô z Ï ô; while (¹Bºâ ); 以 語法說明如 : 1. 不論是前測試或後測試迴圈, 均是運算式值為 真 時, 繼續執行迴 圈, 運算式為 偽 時, 離開迴圈 2. 前測試與後測試迴圈的差別為, 前測試迴圈有可能㆒次均不執行迴圈, 但後測試迴圈至少執行㆒次 3. 後測試迴圈的 while ( 運算式 ) 後面要加分號 (;), 而前測試迴圈的 while 不用加分號 4. while 敘述區塊內亦適用 break 提早離開迴圈 或 continue 略過部份敘 述, 提早進入條件運算式, 決定是否離開迴圈 5. 以 程式片斷使用前測試迴圈統計 1 至 10 的和 i=1; sum=0; while (i<=10) sum=sum+i; i++; 6. 以 程式片斷使用後測試迴圈統計 1 至 10 的和 i=1; sum=0; do sum=sum+i; i++; while (i<=10) ; 5-13
範例 5-2a 假如沒有除法運算子, 請自行使用加減法, 完成除法運算 執行結果 演算分析 1. a= 被除數 2. b= 除數 3. 所謂的商就是被除數 a 共有幾個除數 b, 其迴圈演算法如 : while(a>=b) // 只要被除數大於等於除數, 即執行迴圈敘述 a=a-b ; // 被除數減去㆒個除數 q=q+1 ; // 商值加 1 商 =q ; // 當離開迴圈時, q 即為商 餘數 =a ; // 當離開迴圈時, 所剩的被除數即為餘數 5-14
迴圈敘述 5 程式列印 int a, b; int q=0; a=strtoint(edi1->text); // 被除數 b=strtoint(edi2->text); // 除數 while (a>=b) a-=b; q++; edi3->text=inttostr(q); edi4->text=inttostr(a); void fastcall Tfrm::btnendClick(TObject *Sender) Close(); 程式說明 本程式僅能使用前測試迴圈, 而不能使用後測試迴圈, 因為有可能㆒開始被除數就小於除數 範例 5-2b 請將 10 進位數轉為 N 進位數 ( 本例先討論 N<=9, N>=11 時的狀況, 待陣列介紹之後再詳論 ) 5-15
輸出結果 演算分析 1. a= 待轉換的十進位 2. b=n 進位數 3. 將 a 連續除以 b, 直到整數商為 0, 其餘數的字串累加, 即為 n 進位數, 其迴圈演算法如 : while (a>0) r=a%b ; // 取餘數 strn=inttostr(r)+strn ; // 向左累加餘數 a=a/b ; // 求 a 除以 b 的整數商 //(a 的型別為 int, 除以 n 還是 // 可得 int 型別 )? strn ; // 待離開迴圈, strn 即為 N 進位數 (? 為印出的虛擬指令 ) 5-16
迴圈敘述 5 原理解說 1. 若要將十進位的 a 轉為 2 進位, 則以數學式表示如 : (a) 10 = a 0 + a 1 2 1 + a 2 2 2 + a 3 2 3 + a 4 2 4... = a 0 + 2(a 1 + a 2 2 1 + a 3 2 2 + a 4 2 3...) //a1 以後, 提出 2 2. 式的 a 0 為 a 除以 2 的餘數, a 1 + a 2 2 1 + a 3 2 2 則為 a 除以 2 的整數商, 重覆 式, 直至整數商等於零為止 3. 最先出爐的餘數應放在 2 進位的最右邊 程式列印 (a) 10 = (a n... a 4 a 3 a 2 a 1 a 0 ) 2 int a, b, r; String strn=""; a=strtoint(edi1->text); // 十進位數 b=strtoint(edi2->text); //N 進位數 while (a>0) r=a%b; // 取餘數 strn=inttostr(r)+strn; a=a/b; // 整數商 edi3->text=strn; void fastcall Tfrm::btnendClick(TObject *Sender) Close(); 5-17
程式說明 1. BCB 並無整數商運算子, 所以使用型別轉換的方式, 求取整數商, 也就是商的型別為整數, 其商即為整數 2. 本例進入迴圈之前, 我們並無法預估迴圈次數, 所以較適用 while 迴圈 ( 亦可用 for, 但程式架構較不漂亮 ) 且有可能迴圈㆒次均不執行, 所以要用前測試迴圈 範例 5-2c 請以輾轉相除法, 求兩數的最大公因數 輸出結果 演算分析 1. 輸入 a b 兩數 2. 假如 b>a, 則兩者交換 5-18
迴圈敘述 5 3. 將 a 除以 b, 若餘數不為零, 則以 b 當被除數 (a=b), 以 r 當除數 (b=r), 重 覆 a 除以 b, 直到餘數為零, 促使餘數為零的 b 即為兩數的最大公因數, 其迴圈演算法如 : do r=a%b; a=b; b=r; while (r>0) ; //r 為 a 除以 b 的餘數 程式列印 int a, b, t, r; a=strtoint(edi1->text); b=strtoint(edi2->text); if (b>a) t=a;a=b;b=t; // 兩數交換 do r=a%b; // 取餘數 a=b; b=r; while (r>0) ; // 此處記得使用分號 ; edi3->text=inttostr(a); void fastcall Tfrm::btnendClick(TObject *Sender) Close(); 5-19
程式說明 1. 輾轉相除法的執行之初並無法預估執行次數, 所以較適用 while, 且本 例迴圈至少執行㆒次, 所以用後測試迴圈 2. 後測試迴圈的 while 之後必須加分號 (;), 以免產生錯誤的訊息 5-3 goto 前面㆓節已介紹了結構化程式設計所需使用的迴圈敘述 for 與 while, 原則 使用以 敘述即可解決所有程式設計的問題, 但還是擔心有 不習 慣沒有 goto 的窘境, 所以介紹如, goto 語法如 : goto 標記 ; 標記 : 以 語法說明如 : 1. 標記的命名方式同變數的命名 2. 標記名稱即為 goto 的跳躍點 範例 5-3a 請以 goto 重做範例 5-1a 5-20
迴圈敘述 5 執行結果 程式列印 int i, sum=0; const fontsize=12 ; // 設定常數 const wordsize=fontsize+4; Canvas->Font->Size=fontsize; i=1; bb: Canvas->TextOut(0, wordsize*i, i); sum+=i; //sum=sum+i i++; if (i<=10) goto bb; Canvas->TextOut(0, wordsize*i, " 其和為 " +IntToStr(sum)); void fastcall Tfrm::btnendClick(TObject *Sender) Close(); 5-21
範例 5-3b 請以 goto 重做範例 5-2a 執行結果 程式列印 int a, b; int q=0; a=strtoint(edi1->text); // 被除數 b=strtoint(edi2->text); // 除數 aa: if (a>=b) a-=b; q++; goto aa; edi3->text=inttostr(q); edi4->text=inttostr(a); 5-22
迴圈敘述 5 void fastcall Tfrm::btnendClick(TObject *Sender) Close(); 範例 5-3c 請以 goto 重做範例 5-2c 程式列印 int a, b, t, r; a=strtoint(edi1->text); b=strtoint(edi2->text); if (b>a) t=a;a=b;b=t; // 兩數交換 aa: r=a%b; // 取餘數 a=b; b=r; if (r>0) goto aa ; // 此處記得使用分號 ; edi3->text=inttostr(a); 5-23
5-4 習題 1. 請找出小於等於 100 的所有質數 2. 寫程式求以 級數之和 (n 可輸入 ) 5-24