重绕迭代

Aar*_*ick 2 python iteration

我一直在以简单的方式循环遍历列表的内容以获取用户输入:

for n in [ 1, 2, 3, 4 ]:
 command = raw_input ( "%d >> " % (n) )
 ...
Run Code Online (Sandbox Code Playgroud)

我想实现一个撤销功能,这意味着将迭代"倒回"到之前的值.这是一种天真的方法,它确实减少了n的值,但随后跳过了原始值,因为进入列表的内部指针没有改变:

for n in [ 1, 2, 3, 4 ]:
 if f(n):
  n -= 1
 ...
Run Code Online (Sandbox Code Playgroud)

在文档中我看到了iterator.next(),但没有iterator.last().我想我可以通过整数索引切换到访问列表成员,并通过自己操纵索引来手动循环,这不是那么具有威胁性,但有更好的方法吗?

Jon*_*ice 8

在Python中没有明显的方法可以做到这一点,但是很容易制作支持回溯的自己的迭代器.例如,下面是"可搜索"迭代器.寻求相对0重复相同的元素,或相对-1返回前一个元素.

注意,因为这种迭代方式不享受"保证前进的进步",它可能更容易出现无限循环.

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)
Run Code Online (Sandbox Code Playgroud)

这是一个示例输出:

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
Run Code Online (Sandbox Code Playgroud)