| What is the simplest way to check that you are at the beginning or at
| the end of an iterable? I'm using enumerate with Python 3.2 (see below)
| but I'm wondering if there would be a better way.
|
| l = ['a', 'b', 'a', 'c']
|
| for pos, i in enumerate(l):
| if pos == 0:
| print("head =", i)
| else:
| print(i)
|
| I know that Python is not exactly a functional language but wouldn't
| something like "ishead()" or "istail()" be useful?
There are a few reasons these do not exist out of the box (quite aside
from how easy it is to do on the occasions you actually want it).
Tackling ishead and istail in turn...
The "ishead()" would need to be a top level function (like "len()")
because if it were an iterator method, every iterator would need to have
it implemented; currently the number of methods needed to roll your own
iterator is just two (iter and next). ishead() could be done as a top
level function, though it would need the storage cost of an additional
state value to every iterator (i.e. a "first" boolean or equivalent). So
you'd be proposing more memory cost and possibly a retrospective code
change for all the existing planetwide code, for a minor convenient. As
you note, enumerate gets you a pos value, and it is easy enough to write
a for loop like this:
first = True
for i in iterable_thing:
if first:
print "head =", i
else:
print i
first = False
Your istail() is much worse.
A generator would need to do lookahead to answer istail() in the general
case. Consider iterating over the lines in a file, or better still the
lines coming from a pipeline. Or iteraing over packets received on a
network connection. You can't answer "istail()" there until you have
seen the next line/packet (or EOF/connection close). And that may be an
arbitrary amount of time in the future. You're going to stall your whole
program for such a question?
You can do this easily enough for yourself as an itertools-like thing:
write a wrapper generator that answers ishead() and istail() for
arbitrary iterators. Completely untested example code:
class BoundSensitiveIterator(object):
def __init__(self, subiter):
self.sofar = 0
self.subiter = subiter
self.pending = ()
def iter(self):
return self
def next(self):
self.sofar += 1
if self.pending is None:
raise StopIteration
if self.pending:
nxt = self.pending[0]
self.pending = ()
return nxt
return self.subiter.next()
def ishead(self):
# maybe <= 1, depending on what you want it to mean
return self.sofar == 1
def istail(self):
if self.pending is None:
return True
if self.pending:
return False
try:
nxt = self.subiter.next()
except StopIteration:
self.pending = None
return True
else:
self.pending = (nxt,)
return False
I = BoundSensitiveIterator(other_iterable)
for n in I:
print n, "ishead =", I.ishead(), "istail =", I.istail()
You can see it adds some performance and storage overhead, and of course
may stall if you every ask istail() of an "on demand" iterable.
About the only time I do this is my personal "the()" convenience
function:
def the(list, context=None):
''' Returns the first element of an iterable, but requires there to be
exactly one.
'''
icontext="expected exactly one value"
if context is not None:
icontext=icontext+" for "+context
first=True
for elem in list:
if first:
it=elem
first=False
else:
raise IndexError, "%s: got more than one element (%s, %s, ...)" \
% (icontext, it, elem)
if first:
raise IndexError, "%s: got no elements" % icontext
return it
Which I use as a definite article in places where an iterable _should_
yield exactly one result (eg SQL SELECTs that _ought_ to get exactly
one hit). I can see I wrote that a long time ago - it could do with some
style fixes. And a code scan shows it sees little use
Cheers,
--
Cameron Simpson <
[email protected]> DoD#743
http://www.cskk.ezoshosting.com/cs/
Electronic cardboard blurs the line between printed objects and the virtual
world. - overhead by WIRED at the Intelligent Printing conference Oct2006