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
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
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.
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
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.)
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.
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.
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
while condition is True:
stuff()
else:
stuff()
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
Pourquoi ne fais-tu pas
for s in l :
print s
print "done"
?
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"
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