Emuler une boucle do-while en Python?
-
09-09-2019 - |
Question
Je dois simuler 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, fait", 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 intercepter l'exception « itération stop » et de briser un certain temps boucle correctement?
Un exemple de pourquoi une telle chose peut être nécessaire est indiquée ci-dessous comme pseudo-code.
La 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
La solution
Je ne sais pas ce que vous essayez de faire. Vous pouvez mettre en œuvre une boucle do-while comme ceci:
while True:
stuff()
if fail_condition:
break
Ou:
stuff()
while not fail_condition:
stuff()
Que faites-vous essayer d'utiliser un do while pour imprimer la substance dans 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 garder itérer à travers elle? Que diriez-vous:
for s in l:
while True:
stuff()
# use a "break" instead of s = i.next()
Est-ce que quelque chose semble proche de ce que vous voulez? 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
Autres conseils
Voici un moyen très simple d'imiter 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 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 spectacle accomplit ici ces deux sans avoir besoin d'exceptions ou de casser des déclarations. Il n'introduit une variable booléenne supplémentaire.
Mon code ci-dessous pourrait être une mise en œuvre utile, en soulignant la principale différence entre do-while vs si je comprends bien.
Donc, dans ce un cas, vous allez toujours à travers la boucle au moins une fois.
first_pass = True
while first_pass or condition:
first_pass = False
do_stuff()
Exception cassera la boucle, alors vous pourriez aussi bien 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 l'intérieur break
except
n'est pas défini. En général, break
va un seul niveau, donc par exemple break
l'intérieur try
va directement à finally
(si elle existe) une sortie de la try
, mais pas hors de la boucle.
PEP connexes: http://www.python.org/dev/peps/pep -3136
question connexe: rupture de 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
1) Il est laid. 2) Etat doit être une fonction avec un paramètre, censé être rempli par la substance (c'est la seule raison pour pas pour utiliser le classique en boucle.)
Voici une solution plus fou d'un modèle différent - en utilisant coroutines. Le code est encore 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 vraiment) arrête juste quand vous arrêtez de le nourrir 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 que tuples dans tokens
et je suppose qu'il n'y a pas de différence entre .append()
et .add()
dans le code d'origine.
La façon dont je l'ai fait comme suit ...
condition = True
while condition:
do_stuff()
condition = (<something that evaluates to True or False>)
Cela me semble être la solution simpliste, je suis surpris que je ne l'ai pas vu ici déjà. Cela peut évidemment être inversée à
while not condition:
etc.
pour un 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:
def dowhile(func = None, condition = None):
if not func or not condition:
return
else:
func()
while condition():
func()
Utilisez comme ceci:
>>> x = 10
>>> def f():
... global x
... x = x - 1
>>> def c():
global x
return x > 0
>>> dowhile(f, c)
>>> print x
0
Pourquoi ne faites-vous pas juste
for s in l :
print s
print "done"
Voyez si cela aide:
Définir un indicateur dans le gestionnaire d'exception et vérifier avant de travailler sur le 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 Looping alors qu'une ressource est unavaliable ou quelque chose de similaire qui lance 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