Python:将打印递归函数转换为生成器

时间:2017-01-01 19:23:03

标签: python recursion generator towers-of-hanoi

我找到了这个功能:

def hanoi(pegs, start, target, n):
    assert len(pegs[start]) >= n, 'not enough disks on peg'
    if n == 1:
        pegs[target].append(pegs[start].pop())
        print '%i -> %i: %s' % (start, target, pegs)
    else:
        aux = 3 - start - target  # start + target + aux = 3
        hanoi(pegs, start, aux, n-1)
        hanoi(pegs, start, target, 1)
        hanoi(pegs, aux, target, n-1)

StackOverflow上的Trying to implement recursive Tower of Hanoi algorithm with arrays

现在我需要修改它,以便不是打印start, target, pegs而是每次迭代都会产生pegs变量。

例如,我希望这个输出超出新功能(漂亮打印):

>>> list( hanoi([[120, 90, 60, 30], [], []]) )
[ [[120, 90, 60], [30], []],
  [[120, 90], [30], [60]],
  [[120, 90], [], [60, 30]],
  [[120], [90], [60, 30]],
  [[120, 30], [90], [60]],
  [[120, 30], [90, 60], []],
  [[120], [90, 60, 30], []],
  [[], [90, 60, 30], [120]],
  [[], [90, 60], [120, 30]],
  [[60], [90], [120, 30]],
  [[60, 30], [90], [120]],
  [[60, 30], [], [120, 90]],
  [[60], [30], [120, 90]],
  [[], [30], [120, 90, 60]],
  [[], [], [120, 90, 60, 30]],
]

这是我尝试修改它的方式:

def hanoi(pegs, start, target, n):
    assert len(pegs[start]) >= n, 'not enough disks on peg'
    if n == 1:
        pegs[target].append(pegs[start].pop())
        yield pegs
    else:
        aux = 3 - start - target  # start + target + aux = 3
        hanoi(pegs, start, aux, n-1)
        hanoi(pegs, start, target, 1)
        hanoi(pegs, aux, target, n-1)

但是,(由于图形用途,输入挂钩数字有点大):

>>> pegs = [[120, 90, 60, 30], [], []]
>>> print(list(hanoi(pegs, 0, 2, 4)))
[]

输出只是一个空列表。

尝试通过[:]制作列表的副本没有帮助,我感到非常困惑,也许print总是可以打印出来,但是yield会卡住&#34} #34;在深度递归级别内,因此它被递归到不太深的递归而不是outside。使用append的列表也不起作用:

def hanoi(pegs, start, target, n):
    assert len(pegs[start]) >= n, 'not enough disks on peg'
    out = []
    if n == 1:
        pegs = pegs[:]
        pegs[target].append(pegs[start].pop())
        out.append( pegs )
    else:
        aux = 3 - start - target  # start + target + aux = 3
        hanoi(pegs, start, aux, n-1)
        hanoi(pegs, start, target, 1)
        hanoi(pegs, aux, target, n-1)
    return out

我也尝试过Python: using a recursive algorithm as a generator

的建议
def hanoi(pegs, start, target, n):
    assert len(pegs[start]) >= n, 'not enough disks on peg'
    if n == 1:
        pegs = pegs[:]
        pegs[target].append(pegs[start].pop())
        yield pegs
    else:
        aux = 3 - start - target  # start + target + aux = 3
        for i in hanoi(pegs, start, aux, n-1): yield i
        for i in hanoi(pegs, start, target, 1): yield i
        for i in hanoi(pegs, aux, target, n-1): yield i

从嵌套的for循环中产生,但它失败了。

如何编写此类生成器(我出于图形目的需要)?

生成器将像这样使用:

pegs = [[120, 90, 60, 30], [], []]
positions = hanoi(pegs, 0, 2, 4)

for position in positions:
    screen.fill((255, 255, 255)) 

    print(index, peg_history[index])
    for i, pegs in enumerate(position):
        display_pegs(pegs, 100 + 180*i, 300, screen)
    pygame.display.update()
    time.sleep(0.5)

1 个答案:

答案 0 :(得分:1)

生成器版本可能如下所示:

def hanoi_yield(pegs, start, target, n):
    # pegs will be modified!
    assert len(pegs[start]) >= n, 'not enough disks on peg'

    if n == 1:
        pegs[target].append(pegs[start].pop())
        yield pegs
    else:
        aux = 3 - start - target  # start + target + aux = 3
        yield from hanoi_yield(pegs, start, aux, n-1)
        yield from hanoi_yield(pegs, start, target, 1)
        yield from hanoi_yield(pegs, aux, target, n-1)

pegs = [[120, 90, 60, 30], [], []]
for item in hanoi_yield(pegs, 0, 2, 4):
    print(item)

输出:

[[120, 90, 60], [30], []]
[[120, 90], [30], [60]]
[[120, 90], [], [60, 30]]
[[120], [90], [60, 30]]
[[120, 30], [90], [60]]
[[120, 30], [90, 60], []]
[[120], [90, 60, 30], []]
[[], [90, 60, 30], [120]]
[[], [90, 60], [120, 30]]
[[60], [90], [120, 30]]
[[60, 30], [90], [120]]
[[60, 30], [], [120, 90]]
[[60], [30], [120, 90]]
[[], [30], [120, 90, 60]]
[[], [], [120, 90, 60, 30]]

这里唯一的'技巧'是yield from hanoi_yield,因为hanoi_yield是一个生成器。

down-side:这会一直返回对同一列表的引用,并更改输入列表pegs(这只是返回值)!可能不希望或有用......更多信息如下:

不会更改第一个参数(pegs)并且每次都返回单个列表的版本(因此可以在list构造函数中使用)。我必须添加辅助变量_work_pegs,因为算法需要更改此列表。 pegs现在没有变化。我还yield deepcopy结果(我们在这里处理列表列表;常规副本不起作用):

from copy import deepcopy

def hanoi_yield(pegs, start, target, n, _work_pegs=None):

    if _work_pegs is None:
        _work_pegs = deepcopy(pegs)
        # or (this way pegs could be a tuple of tuples):
        # _work_pegs = [list(item) for item in pegs]

    assert len(_work_pegs[start]) >= n, 'not enough disksdef on peg'

    if n == 1:
        _work_pegs[target].append(_work_pegs[start].pop())
        yield deepcopy(_work_pegs)
        # or (returning tuples might be nice...):
        # yield tuple(tuple(item) for item in _work_pegs)
    else:
        aux = 3 - start - target  # start + target + aux = 3
        yield from hanoi_yield(pegs, start, aux, n-1, _work_pegs)
        yield from hanoi_yield(pegs, start, target, 1, _work_pegs)
        yield from hanoi_yield(pegs, aux, target, n-1, _work_pegs)

最后这个有效:

pegs = [[120, 90, 60, 30], [], []]
lst = list(hanoi_yield(pegs, 0, 2, 4))
print(lst)