Question É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:

l = [ 1, 2, 3 ]
i = l.__iter__()
s = None
while True :
  if s :
    print s
  try :
    s = i.next()
  except StopIteration :
    break
print "done"

Au lieu de "1,2,3, done", il imprime 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 itération' et casser un peu boucle correctement?

Un exemple de pourquoi une telle chose peut être nécessaire est montré ci-dessous comme pseudocode.

Machine d'état:

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

546
2018-04-13 06:18


origine


Réponses:


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 essayant d'utiliser une boucle while do pour imprimer les choses dans la liste? Pourquoi ne pas simplement utiliser:

for i in l:
  print i
print "done"

Mettre à jour:

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

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

Cela ressemble-t-il à quelque chose de 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

703
2018-04-13 06:28



Voici une façon 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 s'exécute toujours au moins une fois, et que la condition est évaluée au bas du corps de la boucle. La structure de contrôle présentée ici accomplit les deux sans avoir besoin d'exceptions ou de déclarations de rupture. Il introduit une variable booléenne supplémentaire.


241
2018-03-14 00:09



Mon code ci-dessous pourrait être une mise en œuvre utile, mettant en évidence la principale différence entre  contre  si je comprends bien.

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

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

51
2017-11-23 23:37



Une exception va casser la boucle, donc vous pourriez aussi bien le 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 ce comportement de break à l'intérieur except n'est pas défini. Généralement break ne monte que d'un niveau, donc par ex. break à l'intérieur try va directement à finally (si elle existe) un sur le try, mais pas hors de la boucle.

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


30
2018-04-13 07:06



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, censé être rempli par des choses (c'est la seule raison ne pas utiliser la boucle while classique.)


28
2018-04-13 13:57



Voici une solution plus folle d'un modèle 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 juste quand 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 recueille tous les jetons en tant que tuples dans tokens et je suppose qu'il n'y a pas de différence entre .append() et .add() dans le code original.


16
2017-11-02 17:32



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 «définitive»

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
2018-05-10 22:03



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

7
2017-11-30 01:38