我已经编写了一个函数来创建任意长度的输入组合,因此递归似乎是显而易见的方法。虽然可以用一个小的玩具示例返回结果列表,但我想改为产生它们。我已经读过yield from
了,但是还没有完全理解它的用法,这些示例似乎还没有涵盖我的用例,并且希望不将其插入我的代码中还没有产生任何东西。作品。请注意,编写此递归代码是我的python能力的极限,因此有大量的调试打印语句。
这是工作清单返回码,其中我希望的非工作收益已被注释掉。
def allposs(elements, output_length):
"""
return all zero insertion paddings of elements up to output_length maintaining order
elements - an iterable of length >= 1
output_length >= len(elements)
for instance allposs((3,1), 4) returns
[[3,1,0,0], [3,0,1,0], [3,0,0,1], [0,3,1,0], [0,3,0,1], [0,0,3,1]]
"""
output_list = []
def place_nth_element(nth, start_at, output_so_far):
# print('entering place_nth_element with nth =', nth,
# ', start_at =', start_at,
# ', output_so_far =', output_so_far)
last_pos = output_length - len(elements) + nth
# print('iterating over range',start_at, 'to', last_pos+1)
for pos in range(start_at, last_pos+1):
output = list(output_so_far)
# print('placing', elements[nth], 'at position', pos)
output[pos] = elements[nth]
if nth == len(elements)-1:
# print('appending output', output)
output_list.append(output)
# yield output
else:
# print('making recursive call')
place_nth_element(nth+1, pos+1, output)
place_nth_element(0, 0, [0]*output_length)
return output_list
if __name__=='__main__':
for q in allposs((3,1), 4):
print(q)
使用yield from
使我的列表一次生成组合的语法是什么?
答案 0 :(得分:2)
递归生成器是一个功能强大的工具,很高兴您正在努力研究它们。
使用yield from来一次生成列表的语法是什么?
您将yield from
放在表达式from
的前面,应该对结果进行yield
;在您的情况下,递归调用。因此:yield from place_nth_element(nth+1, pos+1, output)
。这样做的想法是,在过程的这一点上,将每个结果from
(称为递归生成器)遍历(在幕后)并yield
进行迭代。
请注意,此方法可以起作用:
您需要在递归的基础级别上yield
单个结果
要“收集”结果生成器的结果,您需要遍历顶级调用的结果。幸运的是,迭代内置在很多地方。例如,您可以调用list
,它将为您进行迭代。
我不是将递归生成器嵌套在包装函数中,而是将其编写为单独的辅助函数。由于不再需要从递归访问output_list
,因此无需形成闭包;和flat is better than nested
,如他们所说。但是,这确实意味着我们需要通过递归传递elements
。我们不需要传递output_length
,因为我们可以重新计算它(output_so_far
的长度在递归中是恒定的)。
此外,我发现在执行这类算法时,尽可能地发挥功能(在范式意义上,即避免副作用和可变性,并通过创建新对象来进行思考)是有帮助的。您有一种使用list
进行复制的可行方法(尽管使用.copy
方法更清晰),但是我认为有一种更简洁的方法,如下所示。
所有这些建议使我们能够:
def place_nth_element(elements, nth, start_at, output_so_far):
last_pos = len(output_so_far) - len(elements) + nth
for pos in range(start_at, last_pos+1):
output = output_so_far[:pos] + (elements[nth],) + output_so_far[pos+1:]
if nth == len(elements)-1:
yield output
else:
yield from place_nth_element(elements, nth+1, pos+1, output)
def allposs(elements, output_length):
return list(place_nth_element(elements, 0, 0, (0,)*output_length))
但是,我不会以这种方式解决问题-因为标准库已经提供了一个整洁的解决方案:我们可以找到值应该去到的索引itertools.combinations
,然后将其插入。现在我们不再需要递归地思考,我们可以继续修改值了:)
from itertools import combinations
def place_values(positions, values, size):
result = [0] * size
for position, value in zip(positions, values):
result[position] = value
return tuple(result)
def possibilities(values, size):
return [
place_values(positions, values, size)
for positions in combinations(range(size), len(values))
]