循環迭代

無限輪循

我們一樣從一個簡單的情境範例開始,班上一共有五位學生,如下:

students = ['Kobe', 'MJ', 'LBJ', 'Curry', 'KD']

老師每天都有若干的班級事務需要同學們輪流幫忙完成,為了公平起見,採取輪流的制度,也就是當某一位同學協助了某一次的班務,之後要等到其他四位同學也都各幫忙處理過一件班務之後,才又輪到他。

假設今天班務也用一個清單 tasks 表示,老師可以這樣進行班務的分配:

tasks = 'task1 task2 task3 task4 task5 task6 task7'.split() # 假設今天有 7 項事務

for t_idx, task in enumerate(tasks):
    s_idx = t_idx % len(students)
    student = students[s_idx]
    print(student, task)

結果如下:

Kobe task1
MJ task2
LBJ task3
Curry task4
KD task5
Kobe task6
MJ task7

雖然 Kobe 和 MJ 很倒楣的必須負責兩件公務,但總算是依照規則分配好了,但是這樣的寫法顯然不夠漂亮,這個時候 itertools 中的 cycle 可以幫上我們的忙:

from itertools import cycle

students_pool = cycle(students)

for task in tasks:
    student = next(students_pool) # 從 pool 中抽取下一名學生
    print(student, task)

在這裡,我們利用了 cycle 製作了一個 students_pool,他是一個產生器(迭代器),所以使用 next 方法可以從中拿到下一名學生。cycle 會不斷地輪循 students,所以不論班務有多少都能順利地依照規則選出一名學生來負責。

這樣寫還有另一個好處,students_pool 可以留待隔日繼續使用,下一個抽取出來的學生會是 LBJ 因為循環的狀態被記錄著。於是,此 pool 將可被拿來無限輪循,直到沒有工作為止。

在實務上,我們常會碰到許多需要 分配資源 的代碼。比如說分配伺服器給多個任務以達成分散式運算的目的,我們可以將所有機器的列表 hosts,藉由 cycle 擴充為 hostpool,每個任務都能從 hostpool中抽取出機器,這也保證了一定程度的平均分配。

有限輪循

回到一開始的範例,假設班務的分配僅限於今日(亦或是說任務的數量已知且有限),我們也可以拿 cyclezip 進行搭配,可以寫出更簡潔的代碼:

for student, task in zip(cycle(students), tasks):
    print(student, task)

這樣一來,就可以完全 避免在迴圈中取值 了。

同時我們也能發現,因為 zip 的特性,使得 students 的輪循從無限變成了有限(被 tasks 的數量所限制住)是不是很有趣呢?

results matching ""

    No results matching ""