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