Has Next in Python Iterators

Jussi Piitulainen jpiitula at ling.helsinki.fi
Mon Oct 25 09:47:22 EDT 2010


Kelson Zawack writes:

> The example I have in mind is list like [2,2,2,2,2,2,1,3,3,3,3]
> where you want to loop until you see not a 2 and then you want to
> loop until you see not a 3.  In this situation you cannot use a for
> loop as follows:
...
> because it will eat the 1 and not allow the second loop to find it.
> takeWhile and dropWhile have the same problem.  It is possible to
...

The following may or may not be of interest to you, or to someone
else. If not, please ignore. Probably I misuse some of the technical
terms.

Instead of trying to consume an initial part of an iterable, one can
make a new generator that consumes the underlying iterator in the
desired way. This works nicely if no other code consumes the same
underlying data. My implementation of "after" is below, after the
examples, and after that is an alternative implementation.

  Python 3.1.1 (r311:74480, Feb  8 2010, 14:06:51)
  [GCC 4.4.3] on linux2
  Type "help", "copyright", "credits" or "license" for more information.
  >>> from echoed import *
  >>> list(after(after([3,1,4,1,5], is2), is3))
  [1, 4, 1, 5]
  >>> list(after(after([2,2,3,1,4,1,5], is2), is3))
  [1, 4, 1, 5]
  >>> list(after(after([2,2,1,4,1,5], is2), is3))
  [1, 4, 1, 5]
  >>> list(after(after([2,2], is2), is3))
  []
  >>> list(after(after([3,3], is2), is3))
  []
  >>> list(after(after([], is2), is3))
  []
  >>> list(after(after([1,4,1,5], is2), is3))
  [1, 4, 1, 5]

The implementation of "after" uses two simple auxiliaries, "echoed"
and "evens", which double every item and drop every other item,
respectively. In a sense, "after" returns the echoes of the desired
items: when the first item of interest is encountered in the echoed
stream, its echo remains there.

def echoed(items):
    for item in items:
        yield item
        yield item

def evens(items):
    for item in items:
        yield item
        next(items)

def after(items, is_kind):
    echoed_items = echoed(items)
    try:
        while is_kind(next(echoed_items)):
            next(echoed_items)
    except StopIteration:
        pass
    return evens(echoed_items)

def is2(x):
    return x == 2

def is3(x):
    return x == 3

Alternatively, and perhaps better, one can push the first item of
interest back into a new generator. The auxiliary "later" below does
that; "past" is the alternative implementation of the desired
functionality, used like "after" above.

def later(first, rest):
    yield first
    for item in rest:
        yield item

def past(items, is_kind):
    items = iter(items)
    try:
        item = next(items)
        while is_kind(item):
            item = next(items)
    except StopIteration:
        return items
    return later(item, items)

The names of my functions may not be the best. Many more disclaimers
apply.



More information about the Python-list mailing list