2013-02-27 60 views
2

this PyCon talk的運用收益率,傑克Diederich顯示了這種「簡單」的實施Conway's Game of Life。我並不陌生,無論是GoL的或半高級Python,但代碼似乎很容易掌握,如果不是兩件事情:說明在這個遊戲中生命實現

  1. 採用yield。我之前已經看到使用yield來創建生成器,但是其中的八個是新的...它是否返回一個包含八個生成器的列表,或者這個工具是如何工作的?
  2. set(itertools.chain(*map(neighbors, board)))。這位明星將結果列表(?)從申請鄰居的行爲解開,然後......我的大腦剛剛爆炸。

可能有人試圖解釋這兩個部分一個程序員使用的地圖,過濾器和減少被用於黑客一起一些Python代碼,但是這是不是一個每天都在使用Python? :-)

import itertools 

def neighbors(point): 
    x, y = point 
    yield x + 1, y 
    yield x - 1, y 
    yield x, y + 1 
    yield x, y - 1 
    yield x + 1, y + 1 
    yield x + 1, y - 1 
    yield x - 1, y + 1 
    yield x - 1, y - 1 

def advance(board): 
    newstate = set() 
    recalc = board | set(itertools.chain(*map(neighbors, board))) 
    for point in recalc: 
     count = sum((neigh in board) for neigh in neighbors(point)) 
     if count == 3 or (count == 2 and point in board): 
      newstate.add(point) 
    return newstate 

glider = set([(0,0), (1,0), (2, 0), (0,1), (1,2)]) 
for i in range(1000): 
    glider = advance(glider) 
    print glider 

回答

8

發電機上的兩個原則運作:他們遇到yield聲明每次產生的值,除非它被遍歷,他們的代碼是暫停

不要緊yield語句多少在發電機使用,代碼仍處於正常的Python排序運行。在這種情況下,沒有循環,只是一系列yield語句,所以每次生成器都是高級的時候,python都會執行下一行,這是另一個yield語句。

什麼用neighbors發電機發生是這樣的:

  1. 發電機始終啓動暫停,因此調用neighbors(position)返回並沒有做任何事情一臺發電機。

  2. 當它被提前(調用next())時,代碼將運行到第一個yield語句。首先執行x, y = point,然後計算併產生x + 1, y。代碼再次停頓。

  3. 當再次前進時,代碼將一直運行,直到遇到下一個yield語句。它產生x - 1, y

  4. 等,直到函數完成。

set(itertools.chain(*map(neighbors, board)))線的作用:

  1. map(neighbors, board)產生用於每一個迭代並且在board序列中的每個位置。它只是循環播放,對每個值調用neighbors,並返回結果的新序列。每個neighbors()函數返回一個生成器。

  2. *parameter語法擴展parameter序列插入的參數的列表,就好象函數用在parameter每個元素作爲代替一個單獨的位置參數調用。 param = [1, 2, 3]; foo(*param)將轉化爲foo(1, 2, 3)

    itertools.chain(*map(..))取得由地圖產生的每個發生器,並將其作爲一系列位置參數應用於itertools.chain()。循環鏈式輸出意味着每個電路板位置的每個發生器都按順序迭代一次。

  3. 所有生成位置被添加到組,基本消除重複

你可以展開代碼:

positions = set() 
for board_position in board: 
    for neighbor in neighbors(board): 
     positions.add(neighbor) 

在Python 3,該行可以表達一點代之以更有效地使用itertools.chain.from_iterable(),因爲Python 3中的map()也是一個生成器; .from_iterable()不強制將擴大map(),將根據需要由一個在map()結果,而不是一個循環。

0

它只是返回所有單元格的鄰國的元組。如果你確實理解生成器的作用,那麼在處理大量數據時使用生成器是一個很好的做法。你不需要將所有這些存儲在內存中,只有在你需要時才計算它。

1

哇,這是一個整潔的實現,感謝張貼它!

對於yield,有什麼要補充的Martijn的答案。

至於明星:在map返回一個生成器或列表(取決於蟒蛇2或3),並且該列表中的每一項是發電機(從neighbors),所以我們有發電機的列表。

chain需要很多參數是iterables和鏈條他們,這意味着它返回一個迭代,同時遍歷所有這些反過來。

因爲我們有發電機的名單,並chain需要很多爭論,我們使用一個明星生成的列表轉換爲參數。我們可以用chain.from_iterable完成相同的操作。