2020年10月28日星期三

008 與小卡特一起學 Python 第 8 章 轉圈圈

 申明本站飛宇網 https://feiyetopro.blogspot.com/自網路收集整理之書籍文章影音僅供預覽交流學習研究,其[書籍、文章、影音]情節內容, 評論屬其個人行為, 與本網站無關。版權歸原作者和出版社所有,請在下載 24 小時內刪除,不得用作商業用途;如果您喜歡其作品,請支持訂閱購買[正版]謝謝!


8 章 轉圈圈

對大多數人來說,反復地做同樣的事情很煩人,既然如此,為什麼不讓電腦來為我們做這些事情呢?電腦從來不會覺得煩,所以它們非常擅長去完成重複的任務。在這一章中我們就來看如何讓電腦做重複的事情。

電腦程式通常會周而復始地重複同樣的步驟,這稱為迴圈(looping)。主要有兩種類型的迴圈:

·    重複一定次數的迴圈,稱為計數迴圈(counting loop);

·    重複直至發生某種情況時結束的迴圈,稱為條件迴圈(conditional loop),因為只要條件為真,這種迴圈會一直持續下去。

 



8.1 計數迴圈

第一種迴圈稱為計數迴圈。我們還聽過有人把它叫做 for 迴圈,因為很多語言(包括 Python)在程式中都使用 for 關鍵字來創建這種類型的迴圈。

下面就來嘗試使用計數迴圈的程式。在 IDLE 中使用 File(檔) > New(新建)命令打開一個新的文字編輯器視窗(就像寫第一個程式時一樣)。然後鍵入代碼清單 8-1 中的程式。

代碼清單 8-1 一個非常簡單的 for 迴圈

for looper in [1, 2, 3, 4, 5]:

    print "hello"

 

把它保存為 Loop1.py,運行這個程式(可以使用 Run(運行) > Run Module(運行模組)功能表,也可以用快速鍵 F5)。

你會看到這樣的結果:

>>> ================ RESTART ================

>>> 

hello

hello

hello

hello

hello

 

嘿,是不是有重複?雖然這裡只有一個 print 語句,但程式顯示了 5 “hello”。這是怎麼做到的?第一行(for looper in [1, 2, 3, 4, 5]:)翻譯成我們的語言就表示。


1. 變數 looper 的值從 1 開始(所以 looper = 1)。

2. 對應列表中的每一個值,這個迴圈會把下一個指令塊中的所有工作完成一次。(列表就是中括弧中的那些數)。

3. 每次執行迴圈時,變數 looper 會賦為這個列表中的下一個值。

第二行(print "hello")就是 Python 每次迴圈時要執行的代碼塊。for 迴圈需要一個代碼塊來告訴程式每次迴圈時做什麼。這個代碼塊(縮進的代碼部分)稱為循環體(body of the loop)。(還記得吧?上一章我們討論過縮進和代碼塊。)

術語箱

每次迴圈稱為一次反覆運算(iteration)。

下麵來試試別的。這一次不再是每次都列印相同的東西,下面讓它每次迴圈時列印不同的東西。代碼清單 8-2 就會做這個工作。

代碼清單 8-2 每次 for 迴圈做不同的事情

for looper in [1, 2, 3, 4, 5]:

    print looper

 

把這個程式保存為 Loop2.py,並運行。

結果應該類似於:

>>> ================ RESTART ================

>>> 

1

2

3

4

5

 

這一次不再列印 5 “hello”了,它會列印變數 looper 的值。每次迴圈時,looper 會取列表中的下一個值。

失控的迴圈


卡特,我也遇到過同樣的問題!每一個程式師都曾經遭遇過失控的迴圈(也叫做無限迴圈)。任何時刻(甚至在失控迴圈中)要停止一個 Python 程式,只需要按下 CTRL-C,即按下 CTRL 鍵的同時再按下 C 鍵。以後你會發現這非常方便!遊戲和圖形程式通常都在一個迴圈中運行。這些程式需要不斷從滑鼠、鍵盤或遊戲控制器得到輸入,然後處理這個輸入,並更新螢幕。開始寫這種程式時,我們會大量使用迴圈。所以你的某個程式很有可能會在某一點陷入迴圈,所以你要知道如何讓它脫身!


中括弧做什麼用

你可能已經注意到,迴圈值的列表包圍在中括弧裡。Python 就是利用中括弧以及數之間的逗號來建立列表(list)。稍後就會學習列表(準確地講,是在第 12 章)。不過對現在來說,只要知道列表就是一種容器,可以用來存放一堆東西。在這裡,這些東西就是數,也就是每次迴圈反覆運算時 looper 所取的值。


 8.2 使用計數迴圈

現在利用迴圈做點有意義的事情。下面列印一個乘法表。這裡只對前面的程式做一個小小的修改。這個新版本的程式見代碼清單 8-3

代碼清單 8-3 列印 8 的乘法表

for looper in [1, 2, 3, 4, 5]:

    print looper, "times 8 =", looper * 8

 

把這個程式保存為 Loop3.py,然後運行。你會看到這樣的結果:

>>> =================== RESTART ===================

>>> 

1 times 8 = 8

2 times 8 = 16

3 times 8 = 24

4 times 8 = 32

5 times 8 = 40

 

現在我們終於見識了迴圈的威力。如果沒有迴圈,要得到同樣的結果必須編寫這樣一個程式:

print "1 times 8 =", 1 * 8

print "2 times 8 =", 2 * 8

print "3 times 8 =", 3 * 8

print "4 times 8 =", 4 * 8

print "5 times 8 =", 5 * 8

 

要建立一個更長的乘法表(比如說,從 1 10 或者到 20),這個程式可能會更長,不過我們的迴圈程式幾乎不變(只不過清單中會有更多的數)。迴圈使問題簡單多了!


 8.3 一條捷徑—— range()

在上面的例子中,我們只迴圈了 5 次:

for looper in [1, 2, 3, 4, 5]:

 

如果希望迴圈運行 100 次或者 1000 次呢?這就得鍵入很多很多的數!


很幸運,這裡有一條捷徑。利用 range() 函數,你可以只輸入起始值和結束值,它就會為你創建這二者之間的所有值。range() 會創建一個列表,其中包含某個範圍內的數。

代碼清單 8-4 仍然使用我們在乘法表中用到的例子,不過這裡使用了 range() 函數。

代碼清單 8-4 使用 range() 的迴圈

for looper in range (1, 5):

    print looper, "times 8 =", looper * 8

 

把這個程式保存為 Loop4.py 並運行(可以使用 Run(運行) > Run Module(運行模組)功能表,或者按下快速鍵 F5)。你會看到這樣的結果:

>>> ================= RESTART =================

>>> 

1 times 8 = 8

2 times 8 = 16

3 times 8 = 24

4 times 8 = 32

 

基本上與第一個結果完全相同……不過少了最後一次迴圈!為什麼呢?

答案在於,range (1, 5) 給出的列表是 [1, 2, 3, 4]。你可以在交互模式中試試看:

>>> print range(1, 5)

[1, 2, 3, 4]

 

為什麼沒有 5 呢?

嗯,這正是 range() 函數的做法。它會提供一個數字清單,從給定的第一個數開始,在給定的最後一個數之前結束。必須考慮到這一點,調整範圍來得到想要的迴圈次數。


Range

如果你在 Python 3 中這樣做,結果會有點不一樣:

 

>>> print(range(1, 5))

range(1,5)

這是因為在 Python 3  range() 函數不會提供一個數位清單,而是會提供一個可反覆運算iterable)的東西,你可以使用迴圈來遍歷它。(前文的術語箱中提到過的。)

如果在 for 迴圈中使用 range(),則其工作方式是完全一樣的,只是內部機制略有不同而已。

代碼清單 8-5 給出了修改後的程式,它會給出 8 的乘法表(從 1 10)。

代碼清單 8-5 使用 range() 列印 8 的乘法表(從 1 10

for looper in range(1, 11):

    print looper, "times 8 =", looper * 8

 

運行這個程式時會得到下面的結果:

>>> ================== RESTART ==================

>>> 

1 times 8 = 8

2 times 8 = 16

3 times 8 = 24

4 times 8 = 32

5 times 8 = 40

6 times 8 = 48

7 times 8 = 56

8 times 8 = 64

9 times 8 = 72

10 times 8 = 80

 

在代碼清單 8-5 的程式中,range(1, 11) 給出從 1 10 的一個數字清單,對於清單中的每一個數會完成一次迴圈反覆運算。每次迴圈時,變數 looper 就取清單中的下一個值。

順便說一句,我們把迴圈變數叫做 looper,不過也可以取你喜歡的任何名字。


 8.4 風格問題——迴圈變數名

迴圈變數與其他變數一樣。它沒有任何特殊之處,只是對應一個值的名字而已。將這個變數用作迴圈計數器也是可以的。

之前我們說過,要使用能夠描述變數用途的變數名。正是這個原因,我們在前一個例子中選擇了 looper 這個名字。不過,有時可以有些例外,迴圈變數就屬於這種例外。這是因為,程式設計中有一個慣例(應該記得,慣例就是表示通用的做法),通常使用字母 ijk 等作為迴圈變數。

從前的美好時光


為什麼用 ij  k 迴圈?

這是因為早先的程式師一直用程式來計算數學問題,而數中 abc  xyz 已經有其他用途。另外,在當時一種流的程式設計語言中,變數 ij  k 總是整數,不能把它們創建為何其他類型。由於迴圈計數器總是整數,所以程式師總是選 ij  k 來作為迴圈計數器,這也成為了一種通用的做法。

由於很多人都使用 ijk 作為迴圈變數,程式師在程式中也習慣了這種做法。當然也可以用其他名字作為迴圈變數(就像前面的例子中一樣),不過,除了作為迴圈變數,ijk 不應當有其他用途。

如果採用這個慣例,程式就會像這樣:

for i in range (1, 5):

    print i, "times 8 =", i * 8

 

它的用法完全相同。(你可以試試看!)

為迴圈變數選擇什麼名字屬於風格問題。風格(style)就是你的程式看上去怎麼樣,而與程式能不能正常工作無關。不過,如果與其他程式師採用相同的風格,你的程式就會更易讀、更易於理解,也更易於調試。同時,你也會更加習慣這種風格,能夠更輕鬆地讀懂其他人的程式。

range() 簡寫

不一定非得為 range() 提供兩個數(像在代碼清單 8-5 中那樣),可以只提供一個數:

for i in range (5):

 

這與寫作:

for i in range (0, 5):

 

完全相同,同樣會提供以下數位清單:[0, 1, 2, 3, 4]

實際上,大多數程式師都從 0 開始迴圈而不是從 1 開始。如果使用 range(5),會得到迴圈的 5 次反覆運算,這很容易記住。只是需要知道,第一次迴圈時 i 將等於 0 而不是 1,而最後一次迴圈時,它將等於 4 而不是 5

從前的美好時光


為什麼大多數程式師從 0 而不是 1 開始迴圈呢?

是這樣的,從前,有些人堅持從 1 開始,有些人則堅持從 0 開始。他們對於哪一種做法更好有過激烈的爭論。最終,堅持從 0 開始的人勝利了。

所以就出現了現在的情況,如今大多數人都從 0 開始迴圈,不過你可以根據自己的喜好選擇任何一種做法。只是要記住,需要調整上界來得到正確的反覆運算次數。


嗯,卡特,你已經發現字串的一些規律了。字串就像一個字元清單,我們已經學過:計數迴圈使用列表來完成反覆運算。這說明,也可以利用一個字串來迴圈。字串中的每個字元對應迴圈中的一次反覆運算。所以,如果列印迴圈變數(在這個例子中卡特把他的迴圈變數取名為 letter),就會列印出這個字串中的所有字母,一次列印一個字母。因為每個 print 語句都會換行,所以每個字母分別列印在單獨的一行上。

你可以像卡特一樣,多做一些嘗試,這是一種很好的學習方法!


 8.5 按步長計數

到目前為止,我們的計數迴圈都是每次反覆運算時計數增 1。如果希望迴圈按步長為 2 來計數該怎麼做?或者步長為 5 呢?或者 10 呢?還有,如果想反向計數,又該怎麼做呢?

range() 函數可以有一個額外的參數,利用這個參數可以把步長從默認的 1 改為不同的值。

術語箱

參數(argument)就是使用類似 range() 的函數時放在括弧裡的值。我們說,向函數傳入了參數。有時也用形參(parameter)這個詞,如傳遞形參。我們將在第 13 章瞭解更多關於函數、參數和形參的內容。

我們想在交互模式中嘗試幾個迴圈。鍵入第一行時,由於末尾有冒號,IDLE 會自動為你縮進下一行,因為它知道 for 迴圈後面需要有一個代碼塊。完成這個代碼塊後,按兩次回車鍵。試試看:

>>> for i in range(1, 10, 2):

        print i

1

3

5

7

9

 

這裡向 range() 函數增加了第 3 個參數 2。現在迴圈按步長 2 計數。再來試一個:

>>> for i in range(5, 26, 5):

        print i

5

10

15

20

25

 

這是按步長 5 來迴圈的。反向計數呢?

>>> for i in range(10, 1, -1):

        print i

10

9

8

7

6

5

4

3

2

 

range() 函數中的第 3 個參數是負數時,迴圈會向下計數,而不是向上計數。應該記得,迴圈會從一個數開始,向上(或向下)直到(但不包括)第二個數,所以在最後一個例子中,我們只向下計數到 2,而不是 1


可以利用這一點來建立一個倒計時的計時器程式。只需要再增加兩行代碼。在 IDLE 中打開一個新的編輯器視窗,鍵入代碼清單 8-6 中的程式。試著運行這個程式。

代碼清單 8-6 準備好了嗎?


先不用擔心這個程式裡還沒有講到的內容,比如說 importtime  sleep。所有這些內容都會在後面的章節中講清楚。你只需要試著運行代碼清單 8-6 中的程式,看看它是怎麼工作的。這裡的關鍵是 range(10,0,-1) 部分,它會讓迴圈從 10 反向計數到 1


 8.6 沒有數字的計數

在所有前面的例子中,迴圈變數都是一個數。按程式設計術語來講,可以這麼說:迴圈反覆運算處理一個數位清單。但是清單不一定非得是數字清單。從卡特的試驗我們看到,它也可以是字元清單(一個字串),還可以是一個字串清單,或者是其他列表。

要瞭解它如何工作,最好的辦法就是舉個例子來說明。試著運行代碼清單 8-7 中的程式,看看會發生什麼。

代碼清單 8-7 誰最酷 ?

for cool_guy in ["Spongebob", "Spiderman", "Justin Timberlake", "My Dad"]:

    print cool_guy, "is the coolest guy ever!"

 

現在,我們不再是迴圈處理一個數字清單,這裡會迴圈處理一個字串清單。而且不再將 i 作為迴圈變數,我使用的是 cool_guy。每次迴圈時,迴圈變數 cool_guy 會取清單中一個不同的值。這仍然是一種計數迴圈,因為儘管清單不是數字清單,Python 也要統計列表中有多少項來確定迴圈多少次。(這一次我沒有顯示輸出,你可以自己運行程式來看看結果。)

不過,如果我們無法提前知道需要多少次反覆運算呢?如果沒有可用的值列表呢?別著急,接下來就會講到!


 8.7 關於這個問題……

我們剛才學習了第一種的迴圈,也就是 for 迴圈或計數迴圈。第二種迴圈稱為 while 迴圈或條件迴圈。

如果你能提前知道希望迴圈運行多少次,那麼 for 迴圈很合適。不過,有時你可能希望迴圈一直運行,直到發生某種情況時才結束,而且你不知道發生這種情況之前會有多少次反覆運算。這就可以使用 while 迴圈來實現。

上一章中,我們瞭解了條件和測試,還學習了 if 語句。while 迴圈並不統計運行多少次迴圈,它會使用一個測試來確定什麼時候停止迴圈。while 迴圈也稱為條件迴圈(conditional loop)。條件迴圈會在滿足某個條件時一直保持迴圈。

基本說來,while 迴圈會一直問完了嗎?……完了嗎?……完了嗎?……”,直到完成。它會在條件不再為真時完成。


while 迴圈使用 Python 關鍵字 while。代碼清單 8-8 給出了一個例子。你可以鍵入這個程式,試著運行,看看它是如何工作的。(要記住,一定要先保存再運行。)

代碼清單 8-8 條件或 while 迴圈


這個程式不斷向使用者請求輸入。當輸入等於 3 時,條件為 true,迴圈繼續運行。正是這個原因,這種條件迴圈也稱為 while 迴圈,它使用了 Python  while 關鍵字。輸入不等於 3 時,條件為 false,迴圈停止。


 8.8 跳出迴圈————break  continue

有時可能希望在中間離開迴圈,也就是 for 迴圈結束計數之前,或者 while 迴圈找到結束條件之前。有兩種方法來做到:可以用 continue 直接跳到迴圈的下一次反覆運算,或者用 break 完全中止迴圈。下面會更詳細地說明。


提前跳轉
——continue

如果希望停止執行迴圈的當前反覆運算,提前跳到下一次反覆運算,你需要的就是一條 continue 語句。要說明這一點,最好的辦法就是看一個例子,請看代碼清單 8-9

代碼清單 8-9 迴圈中使用 continue

for i in range (1, 6):

    print

    print 'i =', i,

    print 'Hello, how',

    if i == 3:

        continue

    print 'are you today?'

 

運行這個程式時,輸出如下:

>>> ================== RESTART ==================

>>> 

 

i = 1 Hello, how are you today?

i = 2 Hello, how are you today?

 

i = 3 Hello, how

i = 4 Hello, how are you today?

 

i = 5 Hello, how are you today?

 

注意,第 3 次迴圈時(i == 3),循環體沒有完成,它提前跳到了下一次反覆運算(i == 4)。這就是 continue 語句在起作用。在 while 迴圈中,continue 的作用也是一樣的。

跳出——break

如果我們想完全跳出迴圈——不再完成計數,或者放棄等待結束條件,該怎麼做呢?這個工作由 break 語句完成。

下面只改變代碼清單 8-9 中的第 6 行,把 continue 換成 break,再運行這個程式看看會發生什麼。

>>> ================== RESTART ==================

>>> 

 

i = 1 Hello how are you today?

 

i = 2 Hello how are you today?

 

i = 3 Hello how

 

這一次,迴圈不只是跳過第 3 次反覆運算的其餘部分,它會完全停止迴圈。這正是 break 的作用。在 while 迴圈中,break 的作用也一樣。

要指出的是,有些人認為使用 break  continue 並不好。就我個人來講,我不認為這樣不好,不過我自己確實很少使用這兩個語句。我想還是應該告訴你一些關於 break  continue 的內容,沒准以後你會用到。

你學到了什麼

在這一章,你學到了以下內容。

·    for 迴圈(也稱為計數迴圈)。

·    range() 函數——計數迴圈的一個捷徑。

·    range() 的不同步長大小。

·    while 迴圈(也稱為條件迴圈)。

·     continue 跳到下一次反覆運算。

·     break 跳出迴圈。

測試題

1. 下面的迴圈會運行多少次?

for i in range (1, 6):

    print 'Hi, Warren'

 

2. 下面的迴圈會運行多少次?每次迴圈時 i 的值是什麼?

for i in range (1, 6, 2):

    print 'Hi, Warren'

 

3. range(1, 8) 會給出一個怎樣的數字清單?

4. range(8) 會給出一個怎樣的數字清單?

5. range(2, 9, 2) 會給出一個怎樣的數字清單?

6. range(10, 0, -2) 會給出一個怎樣的數字清單?

7. 使用哪個關鍵字停止迴圈的當前反覆運算,提前跳到下一次反覆運算?

8. while 迴圈什麼時候結束?

動手試一試

1. 編寫一個程式,顯示一個乘法表。開始時要詢問使用者顯示哪個數的乘法表。輸出應該如下所示:

Which multiplication table would you like?

5

Here's your table:

5 x 1 = 5

5 x 2 = 10

5 x 3 = 15

5 x 4 = 20

5 x 5 = 25

5 x 6 = 30

5 x 7 = 35

5 x 8 = 40

5 x 9 = 45

5 x 10 = 50

 

2. 完成第 1 題的程式時你可能使用了 for 迴圈。大多數人都會這麼做。不過,可以再做個練習,試著用 while 迴圈完成同樣的工作。或者如果你在第 1 題中使用了 while 迴圈,現在可以試著用 for 迴圈來完成。

3. 向乘法表程式中再加點東西。詢問用戶想要的乘法表之後,再問問用戶希望最大乘到幾。輸出應當如下所示:

Which multiplication table would you like?

7

How high do you want to go?

12

Here's your table:

7 x 1 = 7

7 x 2 = 14

7 x 3 = 21

7 x 4 = 28

7 x 5 = 35

7 x 6 = 42

7 x 7 = 49

7 x 8 = 56

7 x 9 = 63

7 x 10 = 70

7 x 11 = 77

7 x 12 = 84

 

可以用 for 迴圈或者 while 迴圈的版本來完成,或者兩種做法都試試看。

 

 

0 留言:

發佈留言