вот решение, основанное на коде библиотеки pypy (благодаря предложению agf в комментариях).
состояние доступно через атрибут .state
и может быть сброшено через .goto(state)
, где state
— индекс в последовательности (начиная с 0). в конце есть демо (боюсь, вам нужно прокрутить вниз).
это намного быстрее, чем отбрасывание значений.
> cat prod.py
class product(object):
def __init__(self, *args, **kw):
if len(kw) > 1:
raise TypeError("product() takes at most 1 argument (%d given)" %
len(kw))
self.repeat = kw.get('repeat', 1)
self.gears = [x for x in args] * self.repeat
self.num_gears = len(self.gears)
self.reset()
def reset(self):
# initialization of indicies to loop over
self.indicies = [(0, len(self.gears[x]))
for x in range(0, self.num_gears)]
self.cont = True
self.state = 0
def goto(self, n):
self.reset()
self.state = n
x = self.num_gears
while n > 0 and x > 0:
x -= 1
n, m = divmod(n, len(self.gears[x]))
self.indicies[x] = (m, self.indicies[x][1])
if n > 0:
self.reset()
raise ValueError("state exceeded")
def roll_gears(self):
# Starting from the end of the gear indicies work to the front
# incrementing the gear until the limit is reached. When the limit
# is reached carry operation to the next gear
self.state += 1
should_carry = True
for n in range(0, self.num_gears):
nth_gear = self.num_gears - n - 1
if should_carry:
count, lim = self.indicies[nth_gear]
count += 1
if count == lim and nth_gear == 0:
self.cont = False
if count == lim:
should_carry = True
count = 0
else:
should_carry = False
self.indicies[nth_gear] = (count, lim)
else:
break
def __iter__(self):
return self
def next(self):
if not self.cont:
raise StopIteration
l = []
for x in range(0, self.num_gears):
index, limit = self.indicies[x]
l.append(self.gears[x][index])
self.roll_gears()
return tuple(l)
p = product('abc', '12')
print list(p)
p.reset()
print list(p)
p.goto(2)
print list(p)
p.goto(4)
print list(p)
> python prod.py
[('a', '1'), ('a', '2'), ('b', '1'), ('b', '2'), ('c', '1'), ('c', '2')]
[('a', '1'), ('a', '2'), ('b', '1'), ('b', '2'), ('c', '1'), ('c', '2')]
[('b', '1'), ('b', '2'), ('c', '1'), ('c', '2')]
[('c', '1'), ('c', '2')]
вам следует проверить это еще раз - возможно, я сделал глупую ошибку - но идея довольно проста, поэтому вы сможете ее исправить :о) вы можете использовать мои изменения; понятия не имею, что такое оригинальная лицензия pypy.
также state
на самом деле не является полным состоянием - оно не включает исходные аргументы - это просто индекс в последовательности. возможно, было бы лучше назвать его индексом, но в коде уже есть индикации...
обновить
вот более простая версия, которая имеет ту же идею, но работает путем преобразования последовательности чисел. так что вы просто imap
на count(n)
получаете смещение последовательности на n
.
> cat prod2.py
from itertools import count, imap
def make_product(*values):
def fold((n, l), v):
(n, m) = divmod(n, len(v))
return (n, l + [v[m]])
def product(n):
(n, l) = reduce(fold, values, (n, []))
if n > 0: raise StopIteration
return tuple(l)
return product
print list(imap(make_product(['a','b','c'], [1,2,3]), count()))
print list(imap(make_product(['a','b','c'], [1,2,3]), count(3)))
def product_from(n, *values):
return imap(make_product(*values), count(n))
print list(product_from(4, ['a','b','c'], [1,2,3]))
> python prod2.py
[('a', 1), ('b', 1), ('c', 1), ('a', 2), ('b', 2), ('c', 2), ('a', 3), ('b', 3), ('c', 3)]
[('a', 2), ('b', 2), ('c', 2), ('a', 3), ('b', 3), ('c', 3)]
[('b', 2), ('c', 2), ('a', 3), ('b', 3), ('c', 3)]
(недостаток здесь в том, что если вы хотите остановить и перезапустить, вам нужно следить за тем, сколько вы использовали)
person
andrew cooke
schedule
25.03.2012
product(numbers, *[alnum] * 6)
, и вы могли бы использоватьstring.digits
вместоnumbers
иstring.ascii_lowercase + string.digits
вместоalnum
. - person agf   schedule 26.03.2012