3

このPyCon talkで、Jack Diederich はConway の Game of Life のこの「単純な」実装を示しています。私は GoL にも準高度な Python にも精通していませんが、次の 2 つの点がなければ、コードは非常に簡単に理解できるようです。

  1. の使用yield。以前にyieldを使用してジェネレーターを作成するのを見たことがありますが、8つ続けて使用するのは新しいものです... 8つのジェネレーターのリストを返しますか、またはこれはどのように機能しますか?
  2. set(itertools.chain(*map(neighbors, board))). スターは、隣人をボードに適用した結果のリスト (?) をアンパックします。

map、filter、reduce を使用していくつかの Python コードをハッキングすることに慣れているが、日常的に Python を使用していないプログラマーのために、誰かがこれら 2 つの部分を説明しようとすることはできますか? :-)

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
4

3 に答える 3

11

ジェネレーターは 2 つの原則に基づいて動作します。ステートメントに遭遇するたびに値を生成し、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関数が呼び出されたかのようです。に変換されます。parameterparam = [1, 2, 3]; foo(*param)foo(1, 2, 3)

    itertools.chain(*map(..))は、マップによって生成されたすべてのジェネレーターを受け取り、それを一連の位置パラメーターとして に適用しますitertools.chain()。chain の出力をループするということは、すべてのボード位置のすべてのジェネレーターが順番に 1 回繰り返されることを意味します。

  3. 生成されたすべての位置がセットに追加され、本質的に重複が削除されます

コードを次のように拡張できます。

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

Python 3 では、 Python 3 もジェネレーターであるitertools.chain.from_iterable()ため、代わりにを使用することで、その行をもう少し効率的に表現できます。を強制的に展開するのではなく、必要に応じて結果を 1 つずつループします。map().from_iterable()map()map()

于 2013-02-27T11:51:59.853 に答える
1

うわー、それはきちんとした実装です、投稿してくれてありがとう!

についてはyield、Martijn の回答に追加するものは何もありません。

star に関しては: はmapジェネレーターまたはリスト (python 2 または 3 に依存) を返し、このリストの各項目は (からのneighbors) ジェネレーターであるため、ジェネレーターのリストがあります。

chainiterable である多くの引数を取り、それらをチェーンします。つまり、それらすべてを順番に繰り返しながら、単一の iterable を返します。

ジェネレーターのリストがあり、chain多くの引数を取るため、スターを使用してジェネレーターのリストを引数に変換します。で同じことができたはずchain.from_iterableです。

于 2013-02-27T11:59:47.370 に答える
0

すべてのセルの隣接セルのタプルを返すだけです。ジェネレーターの機能を理解していれば、大量のデータを処理する場合はジェネレーターを使用することをお勧めします。これらすべてをメモリに保存する必要はありません。必要な場合にのみ計算します。

于 2013-02-27T11:53:36.927 に答える