代码之家  ›  专栏  ›  技术社区  ›  Pythoner

曲折迭代器的蟒蛇式方法?

  •  3
  • Pythoner  · 技术社区  · 6 年前

    我正在对之字形迭代器进行编程,它将按以下方式迭代二维列表:

    [1,4,7]
    [2,5,8,9]
    [3,6]
    

    [1,2,3,4,5,6,7,8,9]
    

    我实现了一个算法:

    class ZigzagIterator:
    
        def __init__(self, vecs):
    
            self.vecs = []
            self.turns = 0
            for vec in vecs:
                vec and self.vecs.append(iter(vec))
    
        def next(self):
            try:
                elem = self.vecs[self.turns].next()
                self.turns = (self.turns+1) % len(self.vecs)
                return elem
            except StopIteration:
                self.vecs.pop(self.turns)
                if self.hasNext():
                    self.turns %= len(self.vecs)
    
        def hasNext(self):
            return len(self.vecs) > 0
    
    if __name__ == "__main__":
        s = ZigzagIterator([[1,4,7],[2,5,8,9],[3,6]])
        while s.hasNext():
            print s.next()
    
    >>> 1 2 3 4 5 6 7 8 None None 9 None
    

    我知道问题是因为我对每个列表再调用1次next(),然后得到3个none。我可以通过用Java检查HasNew方法来解决这个问题。我还可以在Python中实现hasNext迭代器。我的问题是如何用更快速的方式解决这个问题,而不是用Java来思考。

    4 回复  |  直到 6 年前
        1
  •  3
  •   Adam Smith jeffpkamp    6 年前

    这是在 itertools docs .

    def roundrobin(*iterables):
        "roundrobin('ABC', 'D', 'EF') --> A D E B F C"
        # Recipe credited to George Sakkis
        num_active = len(iterables)
        nexts = cycle(iter(it).__next__ for it in iterables)
        while num_active:
            try:
                for next in nexts:
                    yield next()
            except StopIteration:
                # Remove the iterator we just exhausted from the cycle.
                num_active -= 1
                nexts = cycle(islice(nexts, num_active))
    
        2
  •  1
  •   AKX Bryan Oakley    6 年前

    这可以很容易地用 itertools :

    from itertools import zip_longest, chain
    
    sentinel = object()
    
    def zigzag(lists):
        return (
            value
            for value
            in chain.from_iterable(zip_longest(*lists, fillvalue=sentinel))
            if value is not sentinel
        )
    
    lists = [
        [1,4,7],
        [2,5,8,9],
        [3,6],
    ]
    
    print(list(zigzag(lists)))
    

    这个 sentinel 需要东西所以 None 值可以安全地进行之字形转换。(这是一个应该保证不会出现在原始列表中的值。)

        3
  •  1
  •   blhsing    6 年前
    from itertools import chain, zip_longest
    print([i for i in chain.from_iterable(zip_longest([1,4,7], [2,5,8,9], [3,6])) if i is not None])
    

    此输出:

    [1, 2, 3, 4, 5, 6, 7, 8, 9]
    
        4
  •  1
  •   Mohit Solanki MatúÅ¡ Bartko    6 年前

    对于您需要实现迭代器协议的pythonic解决方案,我希望这正是您想要的。

    from itertools import chain, zip_longest
    
    
    class ZigZagIterator:
        def __init__(self, *lists):
            self.elements = chain(*zip_longest(*lists))
    
        def __iter__(self):
            for num in self.elements:
                if num is not None:
                    yield num
    
    
    zig = ZigZagIterator([1, 4, 7], [2, 5, 8, 9], [3, 6])
    
    for num in zig:
        print(num)
    

    如果你真的想用 has_next next 然后

    from itertools import chain, zip_longest
    
    
    class ZigZagIterator:
        def __init__(self, *lists):
            elements = chain(*zip_longest(*lists))
            self.elements = filter(lambda x: x is not None, elements)
    
        def has_next(self):
            try:
                self.next_value = next(self.elements)
            except StopIteration:
                return False
            return True
    
        def next(self):
            return self.next_value
    
    
    zig = ZigZagIterator([1, 4, 7], [2, 5, 8, 9], [3, 6])
    
    while zig.has_next():
        print(zig.next())