While loop checking if last loop

How would you go about checking if while loop is in its last loop?

n= 0
z=10

while n<z:

  n+=1
   print(n,z)
    if not last_n:
        print('not last')

Obviously this is simplified code , I’m looping through huge lists where the size is variable.

Your indentation seems a bit odd.

What is the last value? Looks like z-1. Compare n to z-1 at the
top, set a flag. Look at flag when you want to know if it’s the last
loop iteration.

Cheers,
Cameron Simpson cs@cskk.id.au

I’m not the original poster, but I don’t see how to tell when you are in the last loop of a while loop. That requires looking into the future, and I have trouble enough with seeing the past.

from random import random
while random() < 0.9:
    last_loop = ...  # How to decide here???
    if last_loop:
        print("This is the last loop of the while")
        print("no need to break from the loop, because the loop will end")
    else:
        print("the next random number will be less than 0.9")

I know there are many other ways to get the same effect. But in general, I don’t think we can look forward into the future to tell what the next loop condition will be, and so correctly predict when we are in the final loop.

(Inaccurate predictions are easy! It is getting them right which is hard.)

But the OP does not have a random loop, so either the example is very poor or the question is beneath his experience.

If you are looping through a list, huge or not, you probably shouldn’t use a while loop.

There are very few reasons to prefer while loops to for loops when processing lists. Perhaps if you show your actual code, rather than a made-up example, we can advise better?

The randomness is not the important part. The important part is being able to predict the next value of the loop condition ahead of time.

The question is, how to tell when you are in the last iteration of a while loop? That is, given this loop:

while condition:
    block

you need to predict the next value of the condition, while still inside the block. There may be special cases where that is easy:

i = 0
while i < 100:
    if i == 99:
        print("last loop")
    i += 1

but there is no general solution to predicting the future that always works while still keeping the normal structure of a while loop.

If we knew more about the specific test needed, we might be able to work out a solution (I think Cameron already did that for the made up example given) but it’s not always easy to do so.

For a realistic example, consider joiner.join(iterable_of_strings). If one considers the problem as “Follow each item except the last with joiner”, then one would want to know when one is handling the last item. Which in general has the problem Steve discussed.

A workaround for this example is to always add joiner and then delete the last one after the loop completes. Generalized, this is undoing the last doing. In general, the undo may be expensive or impossible.

When possible, the better solution is to recast the problem from treating the last item specially to treating the first item specially, which is easy. For joining, the problem becomes “Prefix each item except the first with joiner”.

def join(joiner, iterable)
    _first = True
    for item in iterable:
        if _first:
            _first = False
        else:
            emit(joiner)
        emit(item)

where emit adds a substring to the string being built.

1 Like

mark-ends is an easy, Pythonic solution to marking the last iteration of an arbitrary iteration–even if it is randomly terminated.

Thanks for the link. I remember now from several years ago. The trick is to wrap the base iterable with an iterator that reads 1 item ahead. The same trick can be used to yield successive overlapping pairs.

The same trick is used by mentalists pretending to read messages in opaque envelops.

That trick, pre-fetching the next item from an iterable before yielding the current item, only works for iterables which are mathematically pure, that is, they have no side-effects, and the items yielded do not depend on the time.

E.g. pre-fetching can give invalid or inaccurate results, up to and including severe security exploits, if the computation can vary over time. The result computed now, during the pre-fetch, may not be the same as the result would have been computed later when you think it is computed.

1 Like

That’s interesting, but it has nothing to do with the question that was asked in which he says that he’s “looping through huge lists where the size is variable.”.

It is something that might be worth mentioning in the mark_ends doc.

I rechecked and the OP’s question is generic and the example did not even involve an iterable.

The answer to the specific question, “How do I know when processing the last item of a list?”, is as trivial as for the OP’s example. For example, the following prints 42.

lenit = [4,3,42]  # len_gth-known it_erable.
n = len(lenit)
for i, item in enumerate(lenit, 1):
    if i == n:
        print('Now at last item:', item)

This will work for any non-empty finite iterable whose length is returned by len().

1 Like

You’re right, of course. I thought there were a few good suggestions in this thread:

  • Cameron’s idea to resuse the loop condition one step back (which you illustrate in your last comment), and
  • your suggestion to do extra processing after the loop to fix things up.

And I wanted to add mark_ends as a readable approach that works for most cases.

Hey, don’t blame me for that! You’re the one that changed the subject to using iteration over an iterator, instead of a while loop.

Mind you, as I said earlier, when processing a list, it is quite unusual to need a while loop. A for loop probably is the correct answer, and as Terry points out, if you are iterating over a list, it is easy to tell when you reach the last item. Or by using more-itertools, if you have a pure iterator

Ultimately, a problem which is tricky or impossible to solve in full generality may be trivial to solve if we know the specific details of the problem.