有没有一种方法可以“暂停”或部分使用Python中的生成器,然后在中断的地方恢复使用?

时间:2018-12-13 06:17:09

标签: python python-3.x iteration generator

有一个相关的问题here。我正在尝试在HackerRank上进行this项目欧拉挑战。它需要的是您能够导出字符串“ abcdefghijklm”的第 n 个排列。有13个!排列。

我尝试了一个使用for num, stry in zip(range(1, math.factorial(13)), itertools.permutations("abcdefghijklm"):的简单解决方案。可行,但超时。

最好的做法是将每个值存储在dict中,然后执行以下操作:

import itertools
import math

strt = "abcdefghijklm"

dic = {}

perms_gen = itertools.permutations(strt)
idxs_gen = range(1, math.factorial(13))

curr_idx = 0

test_list = [1, 2, 5, 10]

def get_elems(n):
  for num, stry in zip(idxs_gen, perms_gen):
    print(num) # debug
    str_stry = "".join(stry)
    dic[num] = str_stry
    if num == n:
      return str_stry

for x in test_list:
  if curr_idx < x:
    print(get_elems(x))
  else:
    print(dic[x])

这不起作用。我得到以下输出:

1
abcdefghijklm
1
2
abcdefghijlkm
1
2
3
4
5
abcdefghikjml
1
2
3
4
5
6
7
8
9
10
abcdefghilmkj

在我写这个问题时,我显然找到了答案……继续。

3 个答案:

答案 0 :(得分:2)

暂停是生成器的内置功能。这是发电机的一半。但是,range 不是生成器。这是一个惰性序列类型。

如果希望对象在其上进行再次迭代将在上次停止的位置继续进行,则需要对范围对象进行迭代:

idsx_iter = iter(range(1, math.factorial(13)))

但是,保存zip迭代器而不是两个基础迭代器会更简单。更好的是,使用enumerate

indexed_permutations = enumerate(itertools.permutations(strt))

不过,您还有很多在代码中没有意义的东西,例如curr_idx永远保持为0,或者range界限可以产生13! -1个索引而不是13个!索引,实际上,您应该使用更有效的算法。例如,一个方法是通过将下一个元素设置为特定字符,然后直接计算每个元素来确定您跳过了多少个排列。

答案 1 :(得分:0)

确定您可以消耗迭代器it的一部分,只需调用next(it)即可消耗一个项目。或者,如果您需要一次消耗多个,则可以编写一个函数来消耗迭代器中的n个项目。在这两种情况下,您只需要注意迭代器尚未结束(引发StopIteration)即可:

def consume(iterator, n):
  for i in range(n):
    try:
      yield next(iterator)
    except StopIteration:
      return

然后可以这样使用:

>>> r = iter(range(5))
>>> print(list(consume(r, 3)))
[0, 1, 2]
>>> print(list(consume(r, 3)))
[3, 4]

最后,我不明白为什么您需要针对这个特定问题使用它,并且如建议的那样,有一个Python函数itertools.permutations已经为您迭代了所有排列。

答案 2 :(得分:-2)

标题中问题的答案为“是”,您可以暂停并重新启动。怎么样?

(对我来说)意外地zip()会重新启动压缩生成器,尽管先前已对其进行了定义(也许有人可以告诉我为什么会这样?)。因此,我添加了main_gen = zip(idxs_gen, perms_gen)并从for num, stry in zip(idxs_gen, perms_gen):更改为for num, stry in main_gen:。然后,我得到以下输出,假设字符串正确,则正是我想要的:

1
abcdefghijklm
2
abcdefghijkml
3
4
5
abcdefghijmkl
6
7
8
9
10
abcdefghiklmj

更改后,代码如下:

import itertools
import math

strt = "abcdefghijklm"

dic = {}

perms_gen = itertools.permutations(strt)
idxs_gen = range(1, math.factorial(13))
main_gen = zip(idxs_gen, perms_gen)

curr_idx = 0

test_list = [1, 2, 5, 10]

def get_elems(n):
  for num, stry in main_gen:
    print(num)
    str_stry = "".join(stry)
    dic[num] = str_stry
    if num == n:
      return str_stry

for x in test_list:
  if curr_idx < x:
    print(get_elems(x))
  else:
    print(dic[x])