2020年7月15日 星期三

Python的惰性求值(Lazy Evaluation)特性

假設有兩組list分別儲存班上八位同學的數學分數及物理分數,每科皆為十分滿分。我們想要找出總分超過12分的同學人數,在Python當中我們可以用zip()將其分數配對成list of tuple,再用迴圈做運算,程式碼如下。輸出結果為4,有四位同學兩科分數加總大於12分。

score_math=[random.randint(0,10) for i in range(8)]
score_phy=[random.randint(0,10) for i in range(8)]
score_table=list(zip(score_math, score_phy))
num=0
for i, j in score_table:
    _sum = i + j
    if _sum > 12:
        num += 1
print(num)

(圖一) 分數資料結構

這組程式碼有一個問題,score_table儲存的資料數值與score_math及score_phy相同,只是排列的方法不同。同一份資料用了兩份記憶體容量意味著記憶體的浪費。一種解決方法是不要產生score_table。取而代之的是在迴圈階段再作取值。比方說,輪到第一個人時,程式到score_math及score_phy找出第一筆資料(3,1),做加總及判斷運算。接著迴圈輪到第二個人時,再到score_math及score_phy找出第二筆資料(9,5)做處理。依此類推直到所有學生的分數都處理完畢為止。要做到這樣的功能,只要在上面的程式碼拿掉zip前面的list()即可。此時變量z就不會在建立的時候被賦予值,而是等到迴圈輪到的時候再做取值的動作,這種延後取值的特性,也被稱之為Lazy Evaluation(惰性求值)。

底下我們用1,000,000筆學生資料來比較list(zip)與zip所使用的時間跟耗費的記憶體,圖二使用了list(zip),耗費220MB, 0.216sec完成計算,圖三使用zip,僅耗費147MB, 0.152秒完成計算,兩者計算結果相同,皆為297871,所耗費的計算資源卻大不相同。惰性求值已經被廣為採用作為加速運算及減少運算資源的方法。

(圖二) 用list(zip)

(圖三) 僅用zip

沒有留言:

張貼留言