Есть ли способ «приостановить» или частично потреблять генератор в Python, а затем возобновить потребление позже, когда он остановился? - PullRequest
0 голосов
/ 13 декабря 2018

Есть связанный вопрос здесь .Я пытаюсь выполнить этот проект Эйлера на HackerRank.Для этого требуется, чтобы вы могли получить n th перестановку строки "abcdefghijklm".Есть 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 голосов
/ 15 апреля 2019

Пауза - это встроенный функционал для генераторов.Это половина дела генераторов.Однако range - это , а не генератор .Это ленивый тип последовательности.

Если вы хотите, чтобы объект, в котором повторение по нему снова продолжилось, где вы в последний раз остановились, вам нужен итератор для объекта диапазона:

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

Однако этобыло бы проще сохранить итератор zip вместо двух базовых итераторов.А еще лучше, используйте enumerate:

indexed_permutations = enumerate(itertools.permutations(strt))

У вас есть намного больше вещей, которые не имеют смысла в вашем коде, например, curr_idx, который просто остается на 0 навсегда, иливаши range границы, которые дают 13! -1 индексов вместо 13!индексы, и действительно, вы должны использовать более эффективный алгоритм.Например, один из них основан на определении количества перестановок, которые вы пропустили, установив следующий элемент для конкретного символа и используя его для непосредственного вычисления каждого элемента перестановки.

0 голосов
/ 15 апреля 2019

Конечно, вы можете использовать часть итератора 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, которая уже перебирает все перестановки для вас.

0 голосов
/ 13 декабря 2018

Ответ на вопрос в заголовке «да», вы можете приостановить и перезапустить.Как?

Неожиданно (для меня), по-видимому, 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])
...