How can I reset a loop that iterates over a set? A common answer for iterating over a list is to reset the index you are using to access the list, however sets do not support indices.
The point is to be able to iterate over a large set of objects, perform some action against each element until a result matches the result I require. The functionality I am searching for is the ability to reset a loop. Meaning restart the iteration from the beginning to ensure I visit every element again for whatever reason.
How can I reset the following loop?
for element in some_set:
print element
if element + offset == needed_result:
# reset
I'm using python 2.7 for this specific problem, but I'd also welcome python 3 solutions.
One way to do so would be by using iterators. You could define an iterator by simply calling iter()
on your set, and call its next
method on each iteration. When the condition is met, you can simply create again the iterator object from the set and repeat the process:
s = {1,2,3,4,5}
s_ = iter(s)
# Just a counter to avoid endless loop
cont = 0
while cont < 10:
try:
i = next(s_)
except StopIteration:
break
# Some condition
if flag == True:
# Reset the iterator when the condition is met
s_ = iter(s)
continue
cont += 1
I would just convert the set to a list and use a while loop:
sett = {'a','b','c','d','e','f','g','h'}
lisst = list(set)
position = 0
while (position <= 8):
element = lisst[position]
print element
if element + offset == needed_result:
position = 0
else:
position += 1
Here's a neat trick that takes any kind of iterable (including sets), and returns an instance of a ReIterable
class (our short name for "resettable iterable").
We can then use the re-iterable in a for
loop as for r in my_reiterable
, just as we would use the original iterable in for i in my_iterable
.
Inside the for
loop, whenever the conditions are right for re-setting the loop, we only have to call the reset()
method on the re-iterable.
Here's an example usage with a set s
(but it could just as well have been a list or any other iterable):
s = {'a', 'b', 'c', 'd', 'e', 'f'}
r = ReIterable(s)
for i in r:
print ('In for loop,', i)
if (i == 'c'):
resp = str(input('Found "c". Do you want to reset?(y/n):'))
if (resp == 'y'):
r.reset()
Here's the output (Note that the first time the user was prompted to reset, the user responded with a y
, causing the set to be iterated all over again. The second time the user was prompted to reset, the user responded with a n
, and the loop proceeded without getting reset):
In for loop, e
In for loop, c
Found "c". Do you want to reset?(y/n):y
In for loop, e
In for loop, c
Found "c". Do you want to reset?(y/n):n
In for loop, a
In for loop, d
In for loop, b
In for loop, f
How does this work? The wrapper class ReIterable makes use of the protocol between a for
loop and an iterable.
Here's what the ReIterable
class looks like (but you only need to know how to use the class, though, as shown above):
class ReIterable:
def __init__(self,original):
self.original = original
self.iterator = iter(original)
self.reset_flag = False
def __iter__(self):
return self
def __next__(self):
if (self.reset_flag):
print ('In __next__(), reset_flag True')
self.iterator = iter(self.original)
self.reset_flag = False
return next(self.iterator)
def reset(self):
self.reset_flag = True
Note:
Besides the fact that it supports the reset operation, the ReIterable
class essentially delegates all the iteration work to the original iterable or its iterator. The actual "resetting" involves junking the old iterator and getting a new one (as also in @yatu's answer).