How to emulate a do-while loop?

PythonWhile LoopDo While

Python Problem Overview


I need to emulate a do-while loop in a Python program. Unfortunately, the following straightforward code does not work:

list_of_ints = [ 1, 2, 3 ]
iterator = list_of_ints.__iter__()
element = None

while True:
  if element:
    print element

  try:
    element = iterator.next()
  except StopIteration:
    break

print "done"

Instead of "1,2,3,done", it prints the following output:

[stdout:]1
[stdout:]2
[stdout:]3
None['Traceback (most recent call last):
', '  File "test_python.py", line 8, in <module>
    s = i.next()
', 'StopIteration
']

What can I do in order to catch the 'stop iteration' exception and break a while loop properly?

An example of why such a thing may be needed is shown below as pseudocode.

State machine:

s = ""
while True :
  if state is STATE_CODE :
    if "//" in s :
      tokens.add( TOKEN_COMMENT, s.split( "//" )[1] )
      state = STATE_COMMENT
    else :
      tokens.add( TOKEN_CODE, s )
  if state is STATE_COMMENT :
    if "//" in s :
      tokens.append( TOKEN_COMMENT, s.split( "//" )[1] )
    else
      state = STATE_CODE
      # Re-evaluate same line
      continue
  try :
    s = i.next()
  except StopIteration :
    break

Python Solutions


Solution 1 - Python

I am not sure what you are trying to do. You can implement a do-while loop like this:

while True:
  stuff()
  if fail_condition:
    break

Or:

stuff()
while not fail_condition:
  stuff()

What are you doing trying to use a do while loop to print the stuff in the list? Why not just use:

for i in l:
  print i
print "done"

Update:

So do you have a list of lines? And you want to keep iterating through it? How about:

for s in l: 
  while True: 
    stuff() 
    # use a "break" instead of s = i.next()

Does that seem like something close to what you would want? With your code example, it would be:

for s in some_list:
  while True:
    if state is STATE_CODE:
      if "//" in s:
        tokens.add( TOKEN_COMMENT, s.split( "//" )[1] )
        state = STATE_COMMENT
      else :
        tokens.add( TOKEN_CODE, s )
    if state is STATE_COMMENT:
      if "//" in s:
        tokens.append( TOKEN_COMMENT, s.split( "//" )[1] )
        break # get next s
      else:
        state = STATE_CODE
        # re-evaluate same line
        # continues automatically

Solution 2 - Python

Here's a very simple way to emulate a do-while loop:

condition = True
while condition:
    # loop body here
    condition = test_loop_condition()
# end of loop

The key features of a do-while loop are that the loop body always executes at least once, and that the condition is evaluated at the bottom of the loop body. The control structure show here accomplishes both of these with no need for exceptions or break statements. It does introduce one extra Boolean variable.

Solution 3 - Python

My code below might be a useful implementation, highlighting the main difference between [tag:do-while] vs [tag:while] as I understand it.

So in this one case, you always go through the loop at least once.

first_pass = True
while first_pass or condition:
    first_pass = False
    do_stuff()

Solution 4 - Python

do {
  stuff()
} while (condition())

->

while True:
  stuff()
  if not condition():
    break

You can do a function:

def do_while(stuff, condition):
  while condition(stuff()):
    pass

But

  1. It's ugly.
  2. Condition should be a function with one parameter, supposed to be filled by stuff (it's the only reason not to use the classic while loop.)

Solution 5 - Python

Exception will break the loop, so you might as well handle it outside the loop.

try:
  while True:
    if s:
      print s
    s = i.next()
except StopIteration:   
  pass

I guess that the problem with your code is that behaviour of break inside except is not defined. Generally break goes only one level up, so e.g. break inside try goes directly to finally (if it exists) an out of the try, but not out of the loop.

Related PEP: http://www.python.org/dev/peps/pep-3136
Related question: https://stackoverflow.com/questions/653509/breaking-out-of-nested-loops

Solution 6 - Python

The way I've done this is as follows...

condition = True
while condition:
     do_stuff()
     condition = (<something that evaluates to True or False>)

This seems to me to be the simplistic solution, I'm surprised I haven't seen it here already. This can obviously also be inverted to

while not condition:

etc.

Solution 7 - Python

Here is a crazier solution of a different pattern -- using coroutines. The code is still very similar, but with one important difference; there are no exit conditions at all! The coroutine (chain of coroutines really) just stops when you stop feeding it with data.

def coroutine(func):
    """Coroutine decorator
    
    Coroutines must be started, advanced to their first "yield" point,
    and this decorator does this automatically.
    """
    def startcr(*ar, **kw):
        cr = func(*ar, **kw)
        cr.next()
        return cr
    return startcr
    
@coroutine
def collector(storage):
    """Act as "sink" and collect all sent in @storage"""
    while True:
        storage.append((yield))
            
@coroutine      
def state_machine(sink):
    """ .send() new parts to be tokenized by the state machine,
    tokens are passed on to @sink
    """ 
    s = ""
    state = STATE_CODE
    while True: 
        if state is STATE_CODE :
            if "//" in s :
                sink.send((TOKEN_COMMENT, s.split( "//" )[1] ))
                state = STATE_COMMENT
            else :
                sink.send(( TOKEN_CODE, s ))
        if state is STATE_COMMENT :
            if "//" in s :
                sink.send(( TOKEN_COMMENT, s.split( "//" )[1] ))
            else
                state = STATE_CODE
                # re-evaluate same line
                continue
        s = (yield)

tokens = []
sm = state_machine(collector(tokens))
for piece in i:
    sm.send(piece)

The code above collects all tokens as tuples in tokens and I assume there is no difference between .append() and .add() in the original code.

Solution 8 - Python

Python 3.8 has the answer.

It's called assignment expressions. from the documentation:

# Loop over fixed length blocks
while (block := f.read(256)) != '':
    process(block)

Solution 9 - Python

I believe that this do-while simulation on python has a syntax format closest to the do-while structure format present in C and Java.

do = True
while do:
    [...]
    do = <condition>

Solution 10 - Python

for a do - while loop containing try statements

loop = True
while loop:
    generic_stuff()
    try:
        questionable_stuff()
#       to break from successful completion
#       loop = False  
    except:
        optional_stuff()
#       to break from unsuccessful completion - 
#       the case referenced in the OP's question
        loop = False
   finally:
        more_generic_stuff()

alternatively, when there's no need for the 'finally' clause

while True:
    generic_stuff()
    try:
        questionable_stuff()
#       to break from successful completion
#       break  
    except:
        optional_stuff()
#       to break from unsuccessful completion - 
#       the case referenced in the OP's question
        break

Solution 11 - Python

while condition is True: 
  stuff()
else:
  stuff()

Solution 12 - Python

Quick hack:

def dowhile(func = None, condition = None):
    if not func or not condition:
        return
    else:
        func()
        while condition():
            func()

Use like so:

>>> x = 10
>>> def f():
...     global x
...     x = x - 1
>>> def c():
        global x
        return x > 0
>>> dowhile(f, c)
>>> print x
0

Solution 13 - Python

Why don't you just do

for s in l :
    print s
print "done"

?

Solution 14 - Python

If you're in a scenario where you are looping while a resource is unavaliable or something similar that throws an exception, you could use something like

import time

while True:
    try:
       f = open('some/path', 'r')
    except IOError:
       print('File could not be read. Retrying in 5 seconds')   
       time.sleep(5)
    else:
       break

Solution 15 - Python

For me a typical while loop will be something like this:

xBool = True
# A counter to force a condition (eg. yCount = some integer value)

while xBool:
	# set up the condition (eg. if yCount > 0):
		(Do something)
		yCount = yCount - 1
	else:
		# (condition is not met, set xBool False)
		xBool = False

I could include a for..loop within the while loop as well, if situation so warrants, for looping through another set of condition.

Solution 16 - Python

You wondered:

> What can I do in order to catch the 'stop iteration' exception and break a while loop properly?

You could do it as shown below and which also makes use of the assignment expressions feature (aka “the walrus operator”) that was introduced in Python 3.8:

list_of_ints = [1, 2, 3]
iterator = iter(list_of_ints)

try:
    while (element := next(iterator)):
        print(element)
except StopIteration:
    print("done")

Another possibility (that would work from Python 2.6 to 3.x) would be to provide a default argument to the built-in next() function to avoid the StopIteration exception:

SENTINEL = object()  # Unique object.
list_of_ints = [1, 2, 3]
iterator = iter(list_of_ints)

while True:
    element = next(iterator, SENTINEL)
    if element is SENTINEL:
        break
    print(element)

print("done")

Solution 17 - Python

While loop:

while condition:
  logic

Do while loop:

while True:
  logic
  if not condition:
    break

Solution 18 - Python

See if this helps :

Set a flag inside the exception handler and check it before working on the s.

flagBreak = false;
while True :

    if flagBreak : break

    if s :
        print s
    try :
        s = i.next()
    except StopIteration :
        flagBreak = true

print "done"

Solution 19 - Python

The built-in iter function does specifically that:

for x in iter(YOUR_FN, TERM_VAL):
    ...

E.g. (tested in Py2 and 3):

class Easy:
  X = 0
  @classmethod
  def com(cls):
    cls.X += 1
    return cls.X

for x in iter(Easy.com, 10):
  print(">>>", x)

If you want to give a condition to terminate instead of a value, you always can set an equality, and require that equality to be True.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiongrigoryvpView Question on Stackoverflow
Solution 1 - PythonTomView Answer on Stackoverflow
Solution 2 - PythonpowderflaskView Answer on Stackoverflow
Solution 3 - Pythonevan54View Answer on Stackoverflow
Solution 4 - PythonZeDView Answer on Stackoverflow
Solution 5 - PythonvartecView Answer on Stackoverflow
Solution 6 - PythonGareth LockView Answer on Stackoverflow
Solution 7 - Pythonu0b34a0f6aeView Answer on Stackoverflow
Solution 8 - PythonJonathan ShemerView Answer on Stackoverflow
Solution 9 - PythonDanilo Matrangolo MaranoView Answer on Stackoverflow
Solution 10 - PythonMarkView Answer on Stackoverflow
Solution 11 - PythonMuShengView Answer on Stackoverflow
Solution 12 - PythonNaftuli KayView Answer on Stackoverflow
Solution 13 - PythonMartinView Answer on Stackoverflow
Solution 14 - PythonAjitView Answer on Stackoverflow
Solution 15 - PythonLove PutinView Answer on Stackoverflow
Solution 16 - PythonmartineauView Answer on Stackoverflow
Solution 17 - PythonnehemView Answer on Stackoverflow
Solution 18 - PythonNrjView Answer on Stackoverflow
Solution 19 - Pythonfr_andresView Answer on Stackoverflow