[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 += ii = 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道題目還沒有用循環的方式做出來,就請繼續思考嘗試,如果始終無法寫出正確的程序,那你現在學編程有點太早了……(但學編程永遠都不會晚)

下一節:《結構體》


推薦閱讀:

[13] 遞歸
[3] 入門問題
學習永遠不晚,只需做到更好
剛好的可能就是最好的

TAG:編程入門 | 編程學習 |