Joining a set of ordered-integer yielding Python iterators

后端 未结 7 2335
囚心锁ツ
囚心锁ツ 2020-12-13 05:33

Here is a seemingly simple problem: given a list of iterators that yield sequences of integers in ascending order, write a concise generator that yields only the integers th

7条回答
  •  悲哀的现实
    2020-12-13 06:20

    If these are really long (or even infinite) sequences, and you don't want to load everything into a set in advance, you can implement this with a 1-item lookahead on each iterator.

    EndOfIter = object() # Sentinel value
    
    class PeekableIterator(object):
        def __init__(self, it):
            self.it = it
            self._peek = None
            self.next() # pump iterator to get first value
    
        def __iter__(self): return self
    
        def next(self):
            cur = self._peek
            if cur is EndOfIter:
                raise StopIteration()
    
            try:
                self._peek = self.it.next()
            except StopIteration:
                self._peek = EndOfIter
            return cur
    
        def peek(self): 
            return self._peek
    
    
    def contained_in_all(seqs):
       if not seqs: return   # No items
       iterators = [PeekableIterator(iter(seq)) for seq in seqs]
       first, rest = iterators[0], iterators[1:]
    
       for item in first:
           candidates = list(rest)
           while candidates:
               if any(c.peek() is EndOfIter for c in candidates): return  # Exhausted an iterator
               candidates = [c for c in candidates if c.peek() < item]
               for c in candidates: c.next()
    
           # Out of loop if first item in remaining iterator are all >= item.
           if all(it.peek() == item for it in rest):
               yield item
    

    Usage:

    >>> print list(contained_in_all(postings))
    [100, 322]
    

提交回复
热议问题