[7] 循環的模式
上一節:《分支、循環與遞歸》
本講涉及的英語單詞(點擊單詞聽讀音):
- while 當
- do...until 做……直到
- for each 對於每一個
- bug 小昆蟲;程序錯誤
- break 打破
- continue 繼續
我們以計算1+2+3+...+100為例,看看循環都有哪些模式:
while 模式
while模式翻譯成人類語句就是「當條件滿足時一直做……」,例如下面的步驟描述:
大筐蘋果數 = 0小筐蘋果數 = 1當(小筐蘋果數 ≤ 100)時一直做:{ 把小筐的蘋果倒進大筐 小筐蘋果數 = 上次小筐蘋果數 + 1}答案 = 大筐蘋果數
程序:
int main(){ int b = 0; int a = 1;p: if (a <= 100) { b = b + a; a = a + 1; goto p; } return b;}
為了使程序更易懂,C語言提供了while語法來實現這種模式:
int main(){ int b = 0; int a = 1; while (a <= 100) { b = b + a; a = a + 1; } return b;}
do...until模式
do...until模式翻譯成人類語言就是「一直做……直到觸發條件才終止」,例如下面的步驟描述:
大筐蘋果數 = 0小筐蘋果數 = 0一直做:{ 小筐蘋果數 = 上次小筐蘋果數 + 1 把小筐的蘋果倒進大筐} 直到(小筐蘋果數 == 100)答案 = 大筐蘋果數
程序:
int main(){ int b = 0; int a = 0;p: a = a + 1; b = b + a; if (a == 100) goto q; goto p;q: return b;}
為了使程序更易懂,C語言提供了do...while語法來實現這種模式:
int main(){ int b = 0; int a = 0; do { a = a + 1; b = b + a; } while (!(a == 100)); return b;}
注意:千萬不要把a == 100
寫成a = 100
,一個等號表示變數賦值,兩個等號是比較運算
說明:
!(a == 100)
的感嘆號是邏輯取反運算符,當x
為真時!x
的結果是0(假),當x
為假時!x
的結果是1(真)。所以!(a == 100)
等價於a != 100
!(a == 100)
的小括弧不能去掉,因為!
的優先順序很高,!a == 100
實際上等價於(!a) == 100
細心的你可能已經發現了,用while (!(a == 100))
來表示until (a ==100)
的語義並不是很直觀(參見開頭的單詞解釋),我們可以用C語言提供的宏定義語法來改善這個問題:
#define until(x) while(!(x))int main(){ int b = 0; int a = 0; do { a = a + 1; b = b + a; } until (a == 100); return b;}
說明:
- 宏的作用就是在預編譯階段,對代碼進行字面上的替換。所以上面兩個程序在預編譯之後,內容是完全一樣的。
for each模式
for each模式翻譯成人類語言就是「對於每一個」,例如下面的步驟描述:
大筐蘋果數 = 0對於每一個1到100間的自然數:{ 大筐蘋果數 = 上次大筐蘋果數 + 這個自然數}答案 = 大筐蘋果數
C語言提供了for語法來實現:
int main(){ int b = 0; for(int i = 1; i <= 100; i = i + 1) { b = b + i; } return b;}
說明:
- for語法的格式為
for(賦初值; 循環條件; 變化方式)
,其中每個部分都可以為空,但分號必須保留。比如for(;;) {...}
依然是符合語法的,跟while(1) {...}
一樣的效果。 - 有的更「現代」的語言(我說的是Swift語言)支持更加直觀的for each語法,像這樣
for i in 1...100 { b = b + i }
。 b = b + i
可以簡寫為b += i
,i = i + 1
可以簡寫為i += 1
,而i += 1
可以進一步簡寫為i++
。同理C語言還支持下面這些增量賦值符號-=
,*=
,/=
,%=
使用增量賦值符號,上面的程序可以簡寫為:
int main(){ int b = 0; for(int i = 1; i <= 100; i++) { b += i; } return b;}
初值bug
在循環外、給循環內的變數賦初值是一個必要步驟!如果沒有賦初值、或者在循環內賦初值、或者初值不正確,則循環的結果就會出錯。讓我們觀察以上三種模式的變數初值都是怎麼賦值的:
while模式: int b = 0; int a = 1;
do...until模式: int b = 0; int a = 0;
for each模式: int b = 0; int i = 1;
賦初值不是靠感覺或者靠蒙,而是依靠對程序精準的分析而確定的。能否花費精力分析程序、模擬程序的運行,寫出的程序能否不出bug,是判斷一個程序員是否優秀的重要標準。
break語法
break可以跳出當前的循環體,例如上面的程序跟如下程序是等效的:
int main(){ int b = 0; for (int i = 1;; i++) { b += i; if (i == 100) { break; } } return b;}
注意:千萬不要把i == 100
寫成i = 100
,一個等號表示變數賦值,兩個等號是比較運算
continue語法
continue可以在當前循環體中,跳過後面的代碼繼續執行下一次循環。例如下面的程序可以計算1+3+5+...+101的值:
int main(){ int b = 0; for (int i = 1;; i++) { if (i % 2 == 0) { continue } if (i > 101) { break; } b += i; } return b;}
說明:
- 在for循環中,continue跳過後面的代碼,但在開始下一次循環前依然會執行
改變方式
(上例中的i++
),否則程序就陷入死循環了(因為i
永遠都等於1) - 在while循環、do...while循環中也可以使用break和continue
- break和continue只作用於當前循環體,如果循環中嵌套了循環,最裡層的break就不能跳出最外層的循環體
下一講我們會介紹結構體。在這之前,如果上一講的4道題目還沒有用循環的方式做出來,就請繼續思考和嘗試,如果始終無法寫出正確的程序,那你現在學編程有點太早了……(但學編程永遠都不會晚)
下一節:《結構體》
推薦閱讀: