I've been looping through the contents of a list in the simple fashion to take user input:
for n in [ 1, 2, 3, 4 ]:
command = raw_input ( "%d >> " % (n) )
...
I want to implement an undo feature, which would mean "rewinding" the iteration back to the previous value. Here is a naive way which does decrease the value of n, but then skips the original value because the internal pointer into the list isn't altered:
for n in [ 1, 2, 3, 4 ]:
if f(n):
n -= 1
...
In the docs I see an iterator.next(), but no iterator.last(). I think I could switch to accessing list members by integer index and hand-roll the loop by manipulating the index myself, which isn't all that threatening, but is there a better way?
There's no obvious way to do this in stock Python, but it's easy enough to make your own iterator that supports backtracking. For example, a "seekable" iterator follows. Seek relative 0 to repeat the same element, or relative -1 to go back to previous element.
NB because this style of iteration doesn't enjoy "guaranteed forward progress," it's arguably more prone to infinite loops.
class SeekableIterator(object):
"""An iterator that supports seeking backwards or forwards."""
def __init__(self, iterable):
"""Make a SeekableIterator over an iterable collection."""
self.iterable = iterable
self.index = None
def __iter__(self):
"""Start the iteration."""
self.index = 0
return self
def next(self):
"""Return the next item in the iterator."""
try:
value = self.iterable[self.index]
self.index += 1
return value
except IndexError:
raise StopIteration
def seek(self, n, relative=False):
"""Adjust the loop counter, either relatively or to an absolute index.
Note that seeking 0 replays the current item. Seeking -1 goes to
the previous item. If the adjustment pushes the index outside the
iterable's bounds, raise an index error."""
if relative:
self.index += n - 1
# NB index already advanced one in next(), so subtracting one here
else:
self.index = n
if self.index < 0 or self.index >= len(self.iterable):
raise IndexError
if __name__ == '__main__':
import random
def prob(percent):
"""Return True with roughly the given probability, else False"""
return random.random() <= (percent * 1.0 / 100.0)
seeker = SeekableIterator([1, 2, 3, 4])
for n in seeker:
print "n:", n
if prob(50):
if prob(50):
print "\tREDO - seeking 0"
seeker.seek(0, relative=True)
elif n > 1:
print "\tUNDO - seeking -1"
seeker.seek(-1, relative=True)
Here is an example output:
n: 1
n: 2
n: 3
n: 4
REDO - seeking 0
n: 4
REDO - seeking 0
n: 4
UNDO - seeking -1
n: 3
n: 4
Use a While loop:
lis = [1, 2, 3, 4]
i = 0
while i < len(lis):
if some_condition:
print(lis[i])
i += 1
elif some_other_condition:
print(lis[i])
i -= 1
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With