The previous answers don't seem to satisfy all four of my obsessive compulsive ticks:
- Be as lazy as possible,
- Evaluate the original Iterable only once
- Evaluate the predicate only once per item
- Provide nice type annotations (for python 3.7)
My solution isn't pretty, and I don't think I can recommend using it, but here it is:
def iter_split_on_predicate(predicate: Callable[[T], bool], iterable: Iterable[T]) -> Tuple[Iterator[T], Iterator[T]]: deque_predicate_true = deque() deque_predicate_false = deque() # define a generator function to consume the input iterable # the Predicate is evaluated once per item, added to the appropriate deque, and the predicate result it yielded def shared_generator(definitely_an_iterator): for item in definitely_an_iterator: print("Evaluate predicate.") if predicate(item): deque_predicate_true.appendleft(item) yield True else: deque_predicate_false.appendleft(item) yield False # consume input iterable only once, # converting to an iterator with the iter() function if necessary. Probably this conversion is unnecessary shared_gen = shared_generator( iterable if isinstance(iterable, collections.abc.Iterator) else iter(iterable) ) # define a generator function for each predicate outcome and queue def iter_for(predicate_value, hold_queue): def consume_shared_generator_until_hold_queue_contains_something(): if not hold_queue: try: while next(shared_gen) != predicate_value: pass except: pass consume_shared_generator_until_hold_queue_contains_something() while hold_queue: print("Yield where predicate is "+str(predicate_value)) yield hold_queue.pop() consume_shared_generator_until_hold_queue_contains_something() # return a tuple of two generators return iter_for(predicate_value=True, hold_queue=deque_predicate_true), iter_for(predicate_value=False, hold_queue=deque_predicate_false)
Testing with the following we get the output below from the print statements:
t,f = iter_split_on_predicate(lambda item:item>=10,[1,2,3,10,11,12,4,5,6,13,14,15])print(list(zip(t,f)))# Evaluate predicate.# Evaluate predicate.# Evaluate predicate.# Evaluate predicate.# Yield where predicate is True# Yield where predicate is False# Evaluate predicate.# Yield where predicate is True# Yield where predicate is False# Evaluate predicate.# Yield where predicate is True# Yield where predicate is False# Evaluate predicate.# Evaluate predicate.# Evaluate predicate.# Evaluate predicate.# Yield where predicate is True# Yield where predicate is False# Evaluate predicate.# Yield where predicate is True# Yield where predicate is False# Evaluate predicate.# Yield where predicate is True# Yield where predicate is False# [(10, 1), (11, 2), (12, 3), (13, 4), (14, 5), (15, 6)]