Zigzag迭代器的Pythonic方法?

时间:2018-07-18 15:43:34

标签: python iterator

我正在使用Zigzag Iterator进行编程,它将以以下方式迭代2D列表:

[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

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

4 个答案:

答案 0 :(得分:3)

这是itertools docs中的循环食谱。

attr

答案 1 :(得分:1)

这可以使用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的值安全地压缩成Z字形。 (应该保证此值不会出现在原始列表中。)

答案 2 :(得分:1)

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]

答案 3 :(得分:1)

对于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_nextnext,那么

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())