跳到內容

迴圈敘述

要讓程式重複做同一件事,總不能一直複製貼上。這一章的迴圈就是拿來重複的:Python 有兩種迴圈,for 和 while。

for 會走過一連串東西,每繞一圈拿出其中一個。最常見的搭配是 range(),它會產生一串數字。range(5) 是從 0 到 4(注意,是到 5 的前一個為止,不包含 5):

for i in range(5):
print(i)

一直按「下一步」,注意變數 i 每繞一圈就換成下一個數字,for 那一行也被一再走過。

range() 最多可以放三個數字,依序是「開始」、「結束」、「間隔」。少寫的會用預設值:開始預設是 0,間隔預設是 1。所以它有三種寫法:

  • range(結束):從 0 開始,到「結束」的前一個為止。
  • range(開始, 結束):從「開始」到「結束」的前一個。
  • range(開始, 結束, 間隔):從「開始」出發,每次跳「間隔」這麼多,到「結束」之前停。

第一種就是上面的 range(5)。第二種給了起點,range(3, 8) 從 3 數到 7:

for i in range(3, 8):
print(i)

第三種多了間隔。range(2, 11, 2) 從 2 開始、每次加 2、到 11 之前為止,也就是 2、4、6、8、10:

for i in range(2, 11, 2):
print(i)

間隔也可以是負數,這時候「開始」要比「結束」大,數字會一路往下數。range(10, 0, -3) 從 10 開始每次減 3,走到 0 之前為止,所以是 10、7、4、1:

for i in range(10, 0, -3):
print(i)

不管哪一種寫法,「結束」那個數字都拿不到,這點很容易記錯。

for ... in ... 的 in 右邊放的是「一串可以一個一個拿出來的東西」,range() 只是其中一種。字串也是,一個字元算一個,所以 for 也能直接走過字串:

for ch in "Python":
print(ch)

後面幾章會學到的串列(list)、元組、字典,也都可以放在 in 的右邊,一圈拿一個元素。像這樣能被 for 一個一個走過的東西,Python 稱為可迭代(iterable)。這裡先知道有這回事,等學到那些型態時會再回頭用 for 走過它們。

迴圈很常搭配一個變數來累加。先把總和設成 0,每繞一圈就加一個數上去:

total = 0
for i in range(1, 11):
total = total + i
print(total)

range(1, 11) 是 1 到 10,全部加起來會得到 55。這種「準備一個變數、在迴圈裡一次一次累加」的寫法,之後會一直用到。

total = total + i 這種「把自己加上某個數,再存回自己」的寫法太常見,Python 給它一個縮寫:total += i,意思完全一樣。下面是同一個程式改用 += 的版本,一步步走過去,看 total 怎麼變:

total = 0
for i in range(1, 11):
total += i
print(total)

其他運算也有同樣的縮寫:-=、*=、/=。例如 count -= 1 就是 count = count - 1。兩種寫法在別人的程式裡都會遇到,自己寫時挑順手的用。

累加從 0 開始很自然,但不是每個問題都有這麼剛好的起點。想從幾個輸入裡找出最小的數字,起始值就沒那麼好設:設 0 的話,只要輸入的都是正數,0 就會一路是最小的,答案錯了。這時候用 None:它是一個特別的值,代表「還沒有值」,正好用來表示還沒看過任何數字。

smallest = None
for i in range(3):
n = int(input("輸入一個數字:"))
if smallest is None or n < smallest:
smallest = n
print("最小的是", smallest)

一開始 smallest 是 None。迴圈裡 if smallest is None or n < smallest 的意思是「如果還沒記過任何數,或這個數更小,就更新」。所以第一圈一定會先記下第一個數,之後才逐一比較。按「執行」,依序輸入三個數字,看右邊 smallest 從 None 變成數字、再被更小的數字換掉。

判斷一個值是不是 None,要用 is None,不要寫 == None。原因是 None 在整個程式裡只有一個,is 問的就是「是不是同一個東西」,答案一定是單純的 True 或 False。== 則是問「值相等嗎」,答案由左邊那個值的型別自己決定;有些型別會改寫 == 的規則,== None 就不一定會回傳 True 或 False。is None 不管左邊放什麼都可靠。

把字串乘上一個數字,會得到重複那麼多次的字串,例如 "*" * 3 是 "***"。搭配迴圈,每圈的重複次數不一樣,就能畫出圖形。這類「星號三角形」是很常見的練習:

for i in range(1, 6):
print("*" * i)

第 i 圈印 i 個星號,所以印出一個一行比一行長的三角形。

while 不數次數,而是只要條件還成立就一直重複。下面這段從 5 倒數,每圈把 count 減 1,等 count 不再大於 0 就停:

count = 5
while count > 0:
print(count)
count = count - 1
print("發射!")

寫 while 一定要確認條件有機會變成不成立(這裡靠 count = count - 1),不然它會一直跑下去,後面「無窮迴圈」那一節會看到那是什麼樣子。

for 能做的事,while 也做得到,只是 for 幫你處理掉的幾件事得自己來:準備一個變數當計數器、每圈加 1、寫停下來的條件。拿前面加總 1 到 10 的例子來改寫:

total = 0
i = 1
while i <= 10:
total += i
i += 1
print(total)

for i in range(1, 11) 那一行,拆開來就是 while 版本的三個部分:起點 i = 1、繼續的條件 i <= 10,還有每圈結尾的 i += 1。少了最後那行 i += 1,i 會永遠停在 1,迴圈就停不下來了。

這個改寫是為了示範兩種迴圈的關係。實務上,事先知道要跑幾圈就用 for,不知道要跑幾圈、只知道什麼時候該停的才用 while。加總本身也不用自己寫迴圈,Python 內建的 sum() 一行就搞定:sum(range(1, 11)) 也是 55。

前面提過 while 的條件要有機會變成不成立。下面是倒數的程式,但少了 count = count - 1 那一行。按「執行」看看會怎樣:

count = 5
while count > 0:
print(count)

count 永遠是 5,count > 0 永遠成立,所以 print(count) 會一直執行,這就是無窮迴圈。這個頁面有保護機制,跑了一定步數之後會自動中止,並顯示「已達步數上限」。在自己的電腦上執行就沒有這層保護,程式會一直印下去,直到你在終端機按下 Ctrl + C 強制結束。

初學時寫出無窮迴圈很正常,多半是忘了改變條件裡的變數,或是條件寫錯方向(例如該寫 > 卻寫成 <)。程式停不下來時,先看 while 的條件用到哪些變數,再檢查迴圈裡有沒有改到它們。

也有故意寫成無窮迴圈的時候:先寫 while True 讓它一直跑,然後在迴圈裡面判斷什麼時候該離開。這要搭配下一章的 break 才做得到,到時候再看。

當你事先不知道要跑幾圈時,while 特別好用。下面這段會一直讀數字、加總,直到使用者輸入 0 為止。按「執行」,依序輸入幾個數字,最後輸入 0 結束:

total = 0
number = int(input("輸入一個數字,輸入 0 結束:"))
while number != 0:
total = total + number
number = int(input("再輸入一個,輸入 0 結束:"))
print("總和是", total)

你可能會在考題或別人的程式裡看到一種更精簡的寫法,用 :=(叫做「海象運算子」,Python 3.8 才加進來)把「讀輸入」和「判斷」合併在 while 那一行:

total = 0
while (n := int(input("輸入數字,輸入 0 結束:"))) != 0:
total = total + n
print("總和是", total)

(n := int(input(...))) 的意思是「把讀到的值存進 n,同時把這個值拿去比較」。它比上面分成兩行的寫法短,但也比較不好一眼看懂。看得懂就好,剛開始學建議還是用上面那種清楚的寫法。

v0.9.1 (c924b5b)