> Hi there,
>
> What is the simplest way to check that you are at the beginning or at the
> end of an iterable?
I don't think this question is meaningful. There are basically two
fundamental types of iterables, sequences and iterators.
Sequences have random access and a length, so if the "start" and "end" of
the sequence is important to you, just use indexing:
beginning = sequence[0]
end = sequence[-1]
for i, x in enumerate(sequence):
if i == 0: print("at the beginning")
elif i == len(sequence)-1: print("at the end")
print(x)
Iterators don't have random access, and in general they don't have a
beginning or an end. There may not be any internal sequence to speak of:
the iterator might be getting data from a hardware device that provides
values continuously, or some other series of values without a well-defined
beginning or end. Example:
def time():
from time import asctime
while True:
yield asctime()
it = time()
What would it even mean to say that I am at the beginning or end of it?
Iterators have no memory, so in one sense you are *always* at the beginning
of the iterator: next() always returns the next item, and the previous item
is lost forever. So the answer to the question "Am I at the beginning of an
iterator?" is always "You are now".
For sequences, the question is best handled differently. For iterators, the
question doesn't make sense in general. If you need an iterator that can
report its internal state, write your own:
import random, time
class MyIter(object):
def __init__(self):
self.start = True
self.end = False
def __next__(self):
if self.start:
self.start = False
if self.end:
raise StopIteration
if random.random() < 0.01:
self.end = True
return time.asctime()
def __iter__(self):
return self
--
Steven
istail can be implemented using itertools.chain, see https://gist.github.com/1202260
> I don't think this question is meaningful. There are basically two
> fundamental types of iterables, sequences and iterators.
And non-sequence iterables like set and dict.
> Sequences have random access and a length, so if the "start" and "end" of
> the sequence is important to you, just use indexing:
>
> beginning = sequence[0]
> end = sequence[-1]
> for i, x in enumerate(sequence):
> if i == 0: print("at the beginning")
> elif i == len(sequence)-1: print("at the end")
> print(x)
And finite non-sequences can be turned into sequences with list(iterable).
--
Terry Jan Reedy
Maybe I should have said "best way to check that you didn't start the iteration process yet" but you see what I mean.
Well I guess I have to unlearn my bad lisp/scheme habits...
For the archives, if Gist ever goes down:
from itertools import chain
def istail(it):
'''Check if iterator has one more element. Return True/False and
iterator.'''
try:
i = next(it)
except StopIteration:
return False, it
try:
j = next(it)
return False, chain([i, j], it)
except StopIteration:
return True, chain([i], it)
t, it = istail(iter([]))
print t, list(it)
t, it = istail(iter([1]))
print t, list(it)
t, it = istail(iter([1, 2]))
print t, list(it)
> 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:-)
A lightweight alternative to that is unpacking:
>>> [x] = ""
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: need more than 0 values to unpack
>>> [x] = "a"
>>> [x] = "ab"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: too many values to unpack