重複的運算
while 迴圈
有的時候,運算我們會想要讓運算能夠重複執行多次,畢竟撰寫多次一樣的運算是個滿無謂的行為,比如說我們想要從1加到10,可以這樣做:
_sum = 0
_sum += 1
_sum += 2
_sum += 3
...
_sum += 10
但這樣重複的作一樣的事情是非常浪費的。
我們會想要用迴圈來讓我們輕易地做到重複,讓我們試試 while
:
_sum = 0
num = 1
while num < 11:
_sum += num
num += 1
while
使用的方法跟 if
很像,我們需要一個條件來判斷 while
的 suite 要不要被執行,記得後面要跟著一個切換到 suite 的冒號,接著屬於 while
的區塊程式碼,都必須以同樣的方式進行縮排。
這跟 if
不同的是,當 while
的 suite 執行完畢之後,他會再度檢查條件,只要符合就會重複地執行 suite 直到某次的檢查發現條件不成立。這樣子不斷地重複、循環,就好像繞行一個圓型路徑一圈又一圈,所以我們又稱這種重複運算的結構為 迴圈。
在上例中,影響迴圈條件的是變數 num
,我們又稱它為 條件變數 或是 迴圈變數,通常一開始我們設定讓迴圈變數能使條件成立,並且在迴圈執行中,更改我們的迴圈變數,在我們想要結束該迴圈的時候使得條件不成立。
num
從 1
開始繞行迴圈,每次的繞行都會遞增 1,這使得 _sum+=num
可以完成 1 加到 10 的任務,更重要的,當 num
增加到 11
時,由於不滿足條件,Python 會讓我們離開迴圈,這樣精準的控制,讓我們剛剛好完成工作。
break 與 continue
如果 while
的條件總是成立,我們稱這種情況為無窮迴圈,基本上來說這是個永不停止的迴圈:
while True:
print('never stop')
這種情況就會非常恐怖了,但是無窮迴圈也有他的價值,當我們的程式必須等待使用者的一些要求時,就必須使用無窮迴圈,讓程式等待使用者的輸入或是命令,並且在處理完該要求後繼續重複地等待下一個要求。
對於迴圈,我們的控制力不只如此,我們還要介紹到兩個強制介入迴圈的關鍵字: break
和 continue
。
break
是用以中斷迴圈的敘述,不論何時,只要碰到 break
,馬上跳出該層迴圈:
n = 5
count = 1
while True:
print('just print {n} times'.format(n=n))
if count == n:
break
count += 1
如果不太了解
format
的話可以上網搜尋一下喔!
這是個比較複雜的例子,乍看之下由於 while
條件的恆真,這似乎是個無窮迴圈,但是我們在 while
中設計了一個巢狀的 if
結構,當 count
的值與 n
的值相同時,因為執行 break
的原因,我們將會跳出該層迴圈。
這邊有幾個概念:
巢狀結構是允許出現在條件結構和重複結構中的,我們可以在
while
中有if
,也允許if
中有while
,甚至while
中有while
,if
中有if
都是可以的。break
跳出的區塊是由他往外看到的 第一個 迴圈區塊,因此他不是跳出if
而是跳出while
,而當迴圈是巢狀結構時,這個判斷就更複雜更需要注意了。
如果今天我們想要印出 1-10 但是不包含 5,我們要怎麼做呢?或許你會想這樣:
num = 1
while num < 5:
print(num)
num += 1
num += 1
while num < 11:
print(num)
num += 1
我們以 5
為分界點,拆成兩段分別撰寫一個迴圈。但是這樣顯然麻煩,我們可以用 continue
幫助我們略過:
num = 1
while num < 11:
if num==5:
num += 1
continue
print(num)
num += 1
continue
敘述會略過之後同屬於該層迴圈區塊的運算,並且重新再開始一次迴圈,意思他會略過該次迴圈,直接進入下一次迴圈。
我們會發現,break
和 continue
通常都要伴隨著條件判斷式,且他們作用的對象是迴圈。
for 迴圈
除了 while
之外,其實還有一個更常用的迴圈: for
迴圈,他會藉由 迭代 來執行重複的運算:
lst = [1, 2, 3]
for num in lst:
print(num)
我們稍微講解一下,首先,如同任何學過的流程控制的結構,對於 for
迴圈,我們必須給定一個冒號並且縮排來進入 suite,至於 for
迴圈要執行幾次並不是靠著條件的判斷來達成,而是靠著迭代的次數。
for item in lst
會將 lst
中的元素一個一個取出,每取出一個便將他賦值(代入)給 item
,並且執行一次 suite,所以,lst
中有幾個元素,我們就會重複幾次,而且每次的 num
值都會是一個 lst
元素的值。
這種依次取出(探訪)並且進行代入的動作稱為迭代。
上面是作者試圖讓初學者理解迭代而自行憑空創詞添意產生的定義,而在維基百科中迭代 (也稱疊代) 的定義是: 疊代是重複反饋過程的活動,其目的通常是為了接近並到達所需的目標或結果。每一次對過程的重複被稱為一次「疊代」,而每一次疊代得到的結果會被用來作為下一次疊代的初始值。
for
迴圈可以對所有可迭代的對象進行迭代,包含清單、元組、字典甚至字串,迭代字典我們會依照任意的順序取出字典的鍵,迭代字串我們會依序取出字串中的字元。
我們可以稍微來剖析一下 for
迴圈跟 while
迴圈的使用時機:
- 當需要重複進行運算的時候使用迴圈 (for/while)
- 當重複的次數可以清楚被計算或當迭代的表現明顯時,使用
for
迴圈 - 當重複的次數難以計算 (但條件清楚) 或是有條件的重複時,使用
while
生成式 (comprehension)
Comprehension 是一種利用 for
迴圈的技術來達成製造清單或字典的快速手段,其實他帶有非常強烈的函數編程的味道,不過這裡不說那麼多,直接看看怎麼做。
假設我們今天有一個清單:
lst = [1, 2, 3, 4, 5]
我們想要製造另一個清單,每個元素是 lst
中元素的平方,如果讀者們還記得清單怎麼加入元素的話,可能會想這樣做:
lst_sq = []
for num in lst:
lst_sq.append(num**2)
我們先設置了一個空的清單: lst_sq
,接著透過 for
來迭代 lst
,取出元素平方後 append
到 lst_sq
上,但是我們有個更簡潔的寫法,請看:
lst_sq = [num**2 for num in lst]
就是那麼簡單,我們直接在清單的特徵-中括號中使用 for
迴圈,取出元素後作平方的動作。這種簡便而快速製造清單的手法就是所謂的 list comprehension (清單生成式)。
讀者們千萬不要誤解,list comprehension 並非一定以清單作為原料,
for
迭代的對象可以是任何可迭代物)。
除了迴圈,我們還能加入條件判斷的應用,比如說,在剛剛的平方數裡,我們只想要留下偶數:
lst_sq = [num**2 for num in lst if num%2==0]
透過 if
我們可以保留下符合條件的元素,其餘的會略過。
要製造字典一樣有 comprehension 的手法:
scores = [88, 90, 100, 65, 78]
score_dic = { student_id:score for student_id, score in enumerate(scores)}
上面的例子稍微複雜,我們解釋一下,我們有個按照學生 id 排好的分數清單 scores
,也就是說 id 為 0 的學生分數是 88,id 為 1 的學生分數是 90,以此類推,我們想要將他化做字典,因此使用 dictionary comprehension。
enumerate
是一個函數,當他和 for
搭配的時候,他會同時把元素跟元素的位置(索引值)取出來,所以我們在迭代時也要用兩個變數來裝載,最後,我們必須用 key:value
的型式將兩個值作一個配對以成為字典的一個元素。
Comprehension 一直都是一個有爭議的手法,許多人其實並不那麼贊成這種表達方式,但是作者認為這些帶有函數編程風格的 Python特性,是相當強力且簡潔的工具,我們應當善加利用。