重複的運算

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,我們又稱它為 條件變數 或是 迴圈變數,通常一開始我們設定讓迴圈變數能使條件成立,並且在迴圈執行中,更改我們的迴圈變數,在我們想要結束該迴圈的時候使得條件不成立。

num1 開始繞行迴圈,每次的繞行都會遞增 1,這使得 _sum+=num 可以完成 1 加到 10 的任務,更重要的,當 num 增加到 11 時,由於不滿足條件,Python 會讓我們離開迴圈,這樣精準的控制,讓我們剛剛好完成工作。

break 與 continue

如果 while 的條件總是成立,我們稱這種情況為無窮迴圈,基本上來說這是個永不停止的迴圈:

while True:
    print('never stop')

這種情況就會非常恐怖了,但是無窮迴圈也有他的價值,當我們的程式必須等待使用者的一些要求時,就必須使用無窮迴圈,讓程式等待使用者的輸入或是命令,並且在處理完該要求後繼續重複地等待下一個要求。

對於迴圈,我們的控制力不只如此,我們還要介紹到兩個強制介入迴圈的關鍵字: breakcontinue

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 的原因,我們將會跳出該層迴圈。

這邊有幾個概念:

  1. 巢狀結構是允許出現在條件結構和重複結構中的,我們可以在 while 中有 if,也允許 if 中有 while,甚至 while 中有 whileif 中有 if 都是可以的。

  2. 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 敘述會略過之後同屬於該層迴圈區塊的運算,並且重新再開始一次迴圈,意思他會略過該次迴圈,直接進入下一次迴圈。

我們會發現,breakcontinue 通常都要伴隨著條件判斷式,且他們作用的對象是迴圈。

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,取出元素平方後 appendlst_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特性,是相當強力且簡潔的工具,我們應當善加利用。

results matching ""

    No results matching ""