決策與迴圈指令

沒有任何一支實用的程式會讓程式指令從頭一直執行到結束,中間不需要處理任何不確定的可能性。

在前一個單元教材中,我們已經陸續看過一些使用決策(if)和迴圈(for)指令的程式範例了,在本單元中就來比較正式地說明這些指令的用法和小技巧。

決策指令

在設計程式的過程中經常會有需要先判斷一個條件是否成立,再決定要做什麼事的情況。例如在計算成績的時候,如果平均成績不及格,就要顯示被當掉的訊息,如果成績優異,也要顯示鼓勵的語句。亦或是,榜單查榜程式在有上榜要沒上榜的時候,顯示的訊息當然也要不一樣。這一類的例子十分常見,此種情況要使用的就是if決策指令。

if

最簡易的決策指令用法如下:

if 條件:
A # 條件成立要執行的區塊

在格式中最重要的部份,除了縮排之外,還有就是在條件後面的冒號「:」。以下是一個簡單的程式例:

score = int(input("請輸入的你國文成績:"))
if score >= 60:
print("恭禧及格過關!")

上面這個程式碼會依照使用者輸入的成續,決定要不要顯示「恭禧及格過關!」的訊息。請再看以下的程式:

score = int(input("請輸入的你國文成績:"))
if score >= 60:
print("你的成績是{}分,".format(score))
print("恭禧及格過關!")

在這個程式中我們多顯示了使用者輸入的成績,因此在輸出的時候會出現兩行訊息,如下所示:

請輸入的你國文成績:90
你的成績是90分,
恭禧及格過關!

同學們看看以下的程式發生了什麼樣的錯誤?

score = int(input("請輸入的你國文成績:"))
if score >= 60:
print("你的成績是{}分,".format(score))
print("恭禧及格過關!")

依照原有的程式的設計邏輯,如果輸入的成績小於60的話,不會有任何訊息顯示,這樣對於使用者來說其實不是很好,因此,在許多情況下我們還需要加上如果條件不成立要處理的程式碼,這時候就需要else的協助。

if/else

延續上一個程式,再加上else功能如下:

score = int(input("請輸入的你國文成績:"))
if score >= 60:
print("你的成績是{}分,".format(score))
print("恭禧及格過關!")
else:
print("你的成績是{}分,".format(score))
print("你的成績不及格耶...")

上面這個程式不管是使用者輸入的分數是多少,都會有對應的顯示訊息,成績不及格的時候,執行的結果如下:

請輸入的你國文成績:45
你的成績是45分,
你的成績不及格耶...

不過,在練習這個程式的時候同學們有沒有發現,第3行和第6行其實是一樣的?沒錯,這是初學者們經常會出現的錯誤,把同樣的程式碼多寫了許多遍。

在這個程式的邏輯中,不管輸入的成績是多少都要顯示出來,因此這一行程式碼是不需要放在if敘述中的,而是要放在if敘述的前面,修改後的程式如下:

score = int(input("請輸入的你國文成績:"))
print("你的成績是{}分,".format(score))
if score >= 60:
print("恭禧及格過關!")
else:
print("你的成績不及格耶...")

if/elif/else

上面判斷國文成績時是只有及格和不及格兩種情況,但世界沒有總是那麼簡單,許多時候我們經常需要對一些清況做好幾個層次的判斷,例如,得到一個成績之後,告訴使用者是什麼等第,如下所示:

score = int(input("請輸入的你國文成績:"))
print("你的成績是{}分,".format(score))
if score >= 90:
print("你的國文等級是A!")
elif score >= 80:
print("你的國文等級是B。")
elif score >= 70:
print("你的國文等級是C。")
elif score>= 60:
print("你的國文等級是D。")
else:
print("你的國文不及格耶!")

以下是其中一個可能的執行結果:

請輸入的你國文成績:75
你的成績是75分,
你的國文等級是C。

我們在程式中是從A開始判斷到不及格,如果同樣的作法把它反過來,先判斷不及格,再逐次判斷到A等第,在程式設計上是否會有什麼問題?

巢狀if敘述

世界是複雜的,有時候一個條件需要多次的判斷才能夠知道要處理的操作或顯示的訊息是什麼,也就是在判斷完一個條件之後,再進一步判斷。以上述的程式例,可以改寫成以下的方式:

score = int(input("請輸入的你國文成績:"))
print("你的成績是{}分,".format(score))
if score < 60:
print("你的國文不及格耶!")
else:
if score >= 90:
print("你的國文等級是A!")
elif score >= 80:
print("你的國文等級是B。")
elif score >= 70:
print("你的國文等級是C。")
elif score>= 60:
print("你的國文等級是D。")

在程式中我們先判斷成績是否及格,因為如果是不及格的話,就不用再浪費那麼多的時間逐一判斷了,只有及格的情況才需要再去看看它是屬於何種等第。這種程式編寫的方式是比較常見的作法。

同學們在程式中要特別留意縮排的使用喔。如果縮排錯誤,程式的執行結果往往會出乎你的意料。

單行的if指令

有些判斷之後往往只會執行一個簡單的指令或僅僅只是設定一個變數值而已,這種情形的if敘述如果把它寫成一行的話,程式看起來會比較簡潔。請參考以下的例子:

score = int(input("請輸入的你國文成績:"))
print("你的成績是{}分,".format(score))
if score < 60:
print("你的國文不及格耶!")
else:
if score >= 90: print("你的國文等級是A!")
elif score >= 80: print("你的國文等級是B。")
elif score >= 70: print("你的國文等級是C。")
elif score>= 60: print("你的國文等級是D。")

還有下面這個例子:

score = int(input("請輸入的你國文成績:"))
result = 'Pass' if score >= 60 else 'Fail'
print("你的測驗結果:{}".format(result))

第2行的if敘述變化的樣子是Python中常見的小技巧。以下是更進階的簡化,當你的程式技巧愈來愈好的時候,你會比較喜歡這樣化簡的方法:

score = int(input("請輸入的你國文成績:"))
result = ('Pass', 'Fail')[score >= 60]
print("你的測驗結果:{}".format(result))

迴圈指令

迴圈指令代表可以讓運算或是操作或是一些作業可以重複地執行許多次,這樣可以節省我們非常多的時間。對初學者來說,重複代表的就是多做幾次,但事實上並不是這麼單純,因為如果只是一樣的運算重複多做幾次會缺乏彈性,真正有威力的是,在重複的過程中,不僅僅是每一次的對象都可以不同,也可以依照每一次的情況決定是否再重複執行,這才是迴圈指令的精要所在。

for迴圈

先來看看單純地重複運算所需要的程式碼。在Python語言中,最常的迴圈程式片段如下:

for i in range(10):
print("Hello")

上述程式中,range(10)是一個用來產生一系列數字的函數,在這個例子中,它會產生如下所示的資料:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

共10個數字值,內容由0到9。而第1行for i in range(10)的意思是表示,讓 i 這個變數分別讓其中的數字逐一代入,每代入一次即執行底下的程式碼區塊一遍,由於數字共有10個,因此底下的程式碼片段會被執行10次。

在迴圈中,程式碼片段只有第2行這一行而已,它的工作是顯示出"Hello"這個字串,因此程式的執行結果就會如下所示:

Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello

就如同學所想的,輸出10個Hello。

其實在程式的每一遍執行的過程中,變數 i 是會隨著代入的內容而更新,因此,我們也可以把變數 i 套入到中間的程式片段,如下所示:

for i in range(10):
print("Hello, line {}.".format(i))

執行的結果如下:

Hello, line 0.
Hello, line 1.
Hello, line 2.
Hello, line 3.
Hello, line 4.
Hello, line 5.
Hello, line 6.
Hello, line 7.
Hello, line 8.
Hello, line 9.

由於那個代入的變數沒有規定一定要使用在迴圈中,因此這樣型式的寫法經常被拿來用於設定要重複的次數。但是,如果需要在重複的過程中也有變數的變化,那麼range函數的使用方法就需要加以瞭解一下。它可以使用的參數如下:

range(start, end, step)

start是啟始值,end是結束值,step是每次的增加值。同學們可以在Shell環境中執行幾個不同的參數看看顯示出來的結果為何,相信就可以瞭解如何運用:

>>> list(range(1, 10, 2))
[1, 3, 5, 7, 9]
>>> list(range(2, 10, 2))
[2, 4, 6, 8]
>>> list(range(10, 1, -1))
[10, 9, 8, 7, 6, 5, 4, 3, 2]
>>> list(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> list(range(10, 2))
[]

瞭解如何設定 range,那麼就讓我們來試著寫一個可以列出九九乘法表的小程式:

for i in range(1, 10):
for j in range(1, 10):
print("{}*{}={}".format(i, j, i*j))

此程式的執行結果如下:

1*1=1
1*2=2
1*3=3
1*4=4
1*5=5
1*6=6
1*7=7
1*8=8
1*9=9
2*1=2
2*2=4
2*3=6
2*4=8
2*5=10
2*6=12
2*7=14
2*8=16
2*9=18
3*1=3
3*2=6
3*3=9
3*4=12
3*5=15
3*6=18
3*7=21
3*8=24
3*9=27
4*1=4
4*2=8
4*3=12
4*4=16
4*5=20
4*6=24
4*7=28
4*8=32
4*9=36
5*1=5
5*2=10
5*3=15
5*4=20
5*5=25
5*6=30
5*7=35
5*8=40
5*9=45
6*1=6
6*2=12
6*3=18
6*4=24
6*5=30
6*6=36
6*7=42
6*8=48
6*9=54
7*1=7
7*2=14
7*3=21
7*4=28
7*5=35
7*6=42
7*7=49
7*8=56
7*9=63
8*1=8
8*2=16
8*3=24
8*4=32
8*5=40
8*6=48
8*7=56
8*8=64
8*9=72
9*1=9
9*2=18
9*3=27
9*4=36
9*5=45
9*6=54
9*7=63
9*8=72
9*9=81

是不是太長了?我們來改進一下:

for i in range(2, 10, 4):
for j in range(1, 10):
print("{}*{}={:2} ".format(i, j, i*j), end="")
print("{}*{}={:2} ".format(i+1, j, (i+1)*j), end="")
print("{}*{}={:2} ".format(i+2, j, (i+2)*j), end="")
print("{}*{}={:2}".format(i+3, j, (i+3)*j))
print()

在第3、4、5行後面的end="",目的是讓後面要輸出的資料不要換行,直接在同一列中接著顯示。

{:2}的目的是為了讓後面的數字在輸出的時候可以固定使用2個格子,如此輸出的排版就會比較整齊。

請留意,第7行的print是故意放在那個地方,它的用途是用來讓兩組之間多了一個空白列用的。

輸出結果如下:

2*1= 2 3*1= 3 4*1= 4 5*1= 5
2*2= 4 3*2= 6 4*2= 8 5*2=10
2*3= 6 3*3= 9 4*3=12 5*3=15
2*4= 8 3*4=12 4*4=16 5*4=20
2*5=10 3*5=15 4*5=20 5*5=25
2*6=12 3*6=18 4*6=24 5*6=30
2*7=14 3*7=21 4*7=28 5*7=35
2*8=16 3*8=24 4*8=32 5*8=40
2*9=18 3*9=27 4*9=36 5*9=45
6*1= 6 7*1= 7 8*1= 8 9*1= 9
6*2=12 7*2=14 8*2=16 9*2=18
6*3=18 7*3=21 8*3=24 9*3=27
6*4=24 7*4=28 8*4=32 9*4=36
6*5=30 7*5=35 8*5=40 9*5=45
6*6=36 7*6=42 8*6=48 9*6=54
6*7=42 7*7=49 8*7=56 9*7=63
6*8=48 7*8=56 8*8=64 9*8=72
6*9=54 7*9=63 8*9=72 9*9=81

透過 range安排適當的數字,再加上兩層迴圈指令的運用,就可以輕易地做出這樣的表格了。也許同學們可能會看出來,其實第3行到第6行也有一定的規律性,因此可以更進一步地再以一層內層的迴圈來化簡程式,如下所示:

for i in range(2, 10, 4):
for j in range(1, 10):
for k in range(0, 4):
print("{}*{}={:2} ".format(i+k, j, i*j), end="")
print()
print()

這個程式和前一個程式會得到一模一樣的結果,但是看起來有沒有更精簡,更有規律了呢!

最後,再來看一下另外一種九九乘法表的印製方法:

for i in range(1,10):
for j in range(1, 10):
print("{:4} ".format(i*j), end="")
print()

以下是輸出的結果,是否和你想的是一樣的呢?

1 2 3 4 5 6 7 8 9
2 4 6 8 10 12 14 16 18
3 6 9 12 15 18 21 24 27
4 8 12 16 20 24 28 32 36
5 10 15 20 25 30 35 40 45
6 12 18 24 30 36 42 48 54
7 14 21 28 35 42 49 56 63
8 16 24 32 40 48 56 64 72
9 18 27 36 45 54 63 72 81

while迴圈

在前面的for迴圈設定中,主要的功能是做為有固定執行次數的程式碼重複之用,但有些情形是不知道一開始要多少次,只有在某些條件符合時才會停止,此種依情況作為是否結束重複執行作業之迴圈方式,我們稱之為條件式迴圈,使用的是while述。以下是while敘述的用法:

while 條件:
重複程式執行區塊

當條件成立時,則會執行一遍重複程式執行區塊,執行一遍之後會再去檢測條件,以決定要不要再來執行一次。在此以底下的這個小程式來做個測試:

import random
number = random.randint(1,12)
while number != 12:
print(number)
number = random.randint(1,12)

在這個程式中我們使用了匯入指令import,它是一個非常方便的指令,可以用來匯入許多優秀程式設計師已經做好的程式模組集,匯入之後直接就可以使用。例如,在這個例子中我們匯入的是 random 模組,它提供了許多和隨機數操作有關的函數,在這裡我們用的是 randint()函數,此函數可以輸入兩個值,第一個值是啟始值,第二個值是結束值,呼叫此函數,它會傳回一個啟始值到結束值之間的任一隨機整數。

在程式中的第2行就利用此函數傳回1到12之間的隨機整數把它放入number這個變數中,接下來在第3行就檢查 number 是否不等於(!=)12,如果此條件成立的話(也就是number不等於12這個條件),就會進入第4行和第5行,分別把number目前的值印出來,然後再使用random.randint(1, 12) 設定一個新的隨機數放到number之中。

由於是隨機取數,所以我們永遠不會知道程式會印出多少數字,以及會印出哪些數字,唯一可以知道的是,一定不會印出12,因為只要是12程式就會立即停止執行,是沒有機會被印出來的。以下是程式其中一個執行的例子:

9
3
10
6
7
1

while迴圈經常被使用在測試是否完成某一條件,如果還沒有滿足的話就持續重複直到條件成立為止。下面這個程式是用來進行10進位轉換成2進位數字的程式,這是一個很典型使用while迴圈的例子:

binary = ""
decimal = int(input("Please enter a number:"))
while decimal > 1:
binary = str(decimal % 2) + binary
decimal = int(decimal / 2)
binary = str(decimal) + binary
print(binary)

這個程式在執行之後會先要求使用者輸入一個數字decimal,然後就檢查decimal是否大於1,如果是的話就先取 2的餘數放到字串中,接著再把它整除2,除2之後再看看是否還大於1,如果是的話就重複之前的運算,如果不是的話則離開迴圈做收尾的工作。以下是執行的結果:

Please enter a number:19
10011