On 07Sep2011 14:35, Laurent <laurent.pa...@gmail.com> wrote: | 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 <c...@zip.com.au> 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 -- http://mail.python.org/mailman/listinfo/python-list