在这个生命游戏实现中解释收益率的使用

时间:2013-02-27 11:45:11

标签: python generator itertools yield conways-game-of-life

this PyCon talk中,Jack Diederich展示了Conway's Game of Life的这种“简单”实现。我对GoL或半高级Python并不熟悉,但代码似乎很容易掌握,如果不是两件事:

  1. 使用yield。我之前看过使用yield来创建生成器,但是它们中的8个是新的......它是否返回了8个生成器的列表,或者这个是如何工作的?
  2. set(itertools.chain(*map(neighbors, board)))。明星解压缩结果列表(?),将邻居应用到董事会,并......我的思绪刚刚爆炸。
  3. 是否有人试图为程序员解释这两个部分,这些程序员习惯于使用map,filter和reduce来篡改一些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
    

3 个答案:

答案 0 :(得分:11)

生成器基于两个原则运行:每次遇到yield语句时它们都会产生一个值,除非它被迭代,否则它们的代码暂停

在生成器中使用了多少yield个语句并不重要,代码仍然以正常的python顺序运行。在这种情况下,没有循环,只有一系列yield语句,所以每次生成器进阶时,python都会执行下一行,这是另一个yield语句。

neighbors生成器会发生什么:

  1. 生成器始终开始暂停,因此调用neighbors(position)会返回尚未执行任何操作的生成器。

  2. 当它处于高级状态(在其上调用next())时,代码将一直运行到第一个yield语句。首先执行x, y = point,然后计算并生成x + 1, y。代码再次暂停。

  3. 再次提升时,代码会一直运行,直到遇到 next yield语句。它产生x - 1, y

  4. 等。直到功能完成。

  5. 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. 所有生成的位置都会添加到一个集合中,基本上会删除重复项

    4. 您可以将代码扩展为:

      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()结果。

答案 1 :(得分:1)

哇,这是一个很好的实现,谢谢发布它!

对于yield,Martijn的答案没有任何补充。

至于星号:map返回一个生成器或列表(取决于python 2或3),此列表中的每个项目都是一个生成器(来自neighbors),所以我们有发电机清单。

chain接受许多可迭代的参数并将它们链接起来,这意味着它会返回一个迭代,同时依次遍历所有参数。

因为我们有一个生成器列表,而chain有很多参数,所以我们使用一个星号将生成器列表转换为参数。我们可以使用chain.from_iterable完成相同的工作。

答案 2 :(得分:0)

它只返回所有单元格邻居的元组。如果您确实了解了生成器的作用,很明显在处理大量数据时使用生成器是一种很好的做法。你不需要将所有这些存储在内存中,只有在需要时才计算它。