web-dev-qa-db-fra.com

Émuler une boucle Do-While en Python?

J'ai besoin d'émuler une boucle do-while dans un programme Python. Malheureusement, le code simple suivant ne fonctionne pas:

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"

Au lieu de "1,2,3, terminé", il affiche la sortie suivante:

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

Que puis-je faire pour attraper l'exception 'stop iteration' et briser correctement une boucle while?

Un exemple de la raison pour laquelle une telle chose peut être nécessaire est présenté ci-dessous en tant que pseudocode.

Machine à états:

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
702
grigoryvp

Je ne suis pas sûr de ce que vous essayez de faire. Vous pouvez implémenter une boucle do-while comme ceci:

while True:
  stuff()
  if fail_condition:
    break

Ou:

stuff()
while not fail_condition:
  stuff()

Que faites-vous en utilisant une boucle Do While pour imprimer les éléments de la liste? Pourquoi ne pas simplement utiliser:

for i in l:
  print i
print "done"

Mise à jour:

Alors avez-vous une liste de lignes? Et vous voulez continuer à itérer? Que diriez-vous:

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

Cela vous semble-t-il proche de ce que vous voudriez? Avec votre exemple de code, ce serait:

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
875
Tom

Voici un moyen très simple d'émuler une boucle do-while:

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

Les principales caractéristiques d'une boucle do-while sont que le corps de la boucle est toujours exécuté au moins une fois et que la condition est évaluée au bas du corps de la boucle. La structure de contrôle montre ici que cela accomplit les deux sans avoir besoin d'exceptions ni de déclarations break. Il introduit une variable booléenne supplémentaire.

276
powderflask

Mon code ci-dessous pourrait être une implémentation utile, soulignant la principale différence entre à faire vs while tel que je le comprends.

Donc, dans ce cas, vous parcourez toujours la boucle au moins une fois.

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

Exception cassera la boucle, aussi bien la gérer en dehors de la boucle.

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

Je suppose que le problème avec votre code est que le comportement de break à l'intérieur de except n'est pas défini. Généralement, break n'augmente qu'un niveau, donc par ex. break à l'intérieur try va directement à finally (s'il existe) une sortie de la try, mais pas de la boucle.

PEP lié: http://www.python.org/dev/peps/pep-3136
Question connexe: Sortie des boucles imbriquées

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

->

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

Vous pouvez faire une fonction:

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

Mais 1) c'est moche. 2) La condition devrait être une fonction avec un paramètre, supposée être remplie par des choses (c'est la seule raison pas d'utiliser la boucle while classique.)

31
ZeD

Voici une solution plus folle d'un motif différent - en utilisant des coroutines. Le code est toujours très similaire, mais avec une différence importante; il n'y a pas de conditions de sortie du tout! La coroutine (chaîne de coroutines) s’arrête lorsque vous arrêtez de l’alimenter avec des données.

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)

Le code ci-dessus rassemble tous les jetons sous forme de tuples dans tokens et je suppose qu'il n'y a pas de différence entre .append() et .add() dans le code d'origine.

16
u0b34a0f6ae

Voici comment je l'ai fait.

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

Cela me semble être la solution simpliste, je suis surpris de ne pas l'avoir déjà vue ici. Ceci peut évidemment aussi être inversé pour

while not condition:

etc.

11
Gareth Lock

pour une boucle do - while contenant des instructions try

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()

alternativement, quand il n'y a pas besoin de la clause "enfin"

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
10
Mark
while condition is True: 
  stuff()
else:
  stuff()
7
MuSheng

Hack rapide:

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

Utilisez comme si:

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

Pourquoi ne fais-tu pas

for s in l :
    print s
print "done"

?

4
Martin

Voir si cela aide:

Définissez un indicateur dans le gestionnaire d'exceptions et vérifiez-le avant de travailler sur s.

flagBreak = false;
while True :

    if flagBreak : break

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

print "done"
1
Nrj

Si vous êtes dans un scénario où vous faites une boucle alors qu'une ressource est indisponible ou quelque chose de similaire qui lève une exception, vous pouvez utiliser quelque chose comme:

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
1
Ajit