switch - while anidado python
¿Emular un bucle do-while en Python? (13)
Necesito emular un bucle do-while en un programa de Python. Desafortunadamente, el siguiente código simple no funciona:
l = [ 1, 2, 3 ]
i = l.__iter__()
s = None
while True :
if s :
print s
try :
s = i.next()
except StopIteration :
break
print "done"
En lugar de "1,2,3, hecho", imprime el siguiente resultado:
[stdout:]1
[stdout:]2
[stdout:]3
None[''Traceback (most recent call last):
'', '' File "test_python.py", line 8, in <module>
s = i.next()
'', ''StopIteration
'']
¿Qué puedo hacer para detectar la excepción ''parar iteración'' y romper un bucle de tiempo correctamente?
Un ejemplo de por qué tal cosa puede ser necesaria se muestra a continuación como pseudocódigo.
Máquina estatal:
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
A ver si esto ayuda:
Coloque una bandera dentro del controlador de excepciones y verifíquela antes de trabajar en el s.
flagBreak = false;
while True :
if flagBreak : break
if s :
print s
try :
s = i.next()
except StopIteration :
flagBreak = true
print "done"
Aquí hay una manera muy simple de emular un bucle do-while:
condition = True
while condition:
# loop body here
condition = test_loop_condition()
# end of loop
Las características clave de un bucle do-while son que el cuerpo del bucle siempre se ejecuta al menos una vez, y que la condición se evalúa en la parte inferior del cuerpo del bucle. La estructura de control que se muestra aquí cumple ambos objetivos sin necesidad de excepciones ni declaraciones de interrupción. Se introduce una variable booleana extra.
Aquí hay una solución más loca de un patrón diferente: usar coroutines. El código sigue siendo muy similar, pero con una diferencia importante; ¡No hay condiciones de salida en absoluto! La coroutine (cadena de coroutines en realidad) simplemente se detiene cuando deja de alimentarla con datos.
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)
El código anterior recopila todos los tokens como tuplas en tokens
y asumo que no hay diferencia entre .append()
y .add()
en el código original.
Corte rápido:
def dowhile(func = None, condition = None):
if not func or not condition:
return
else:
func()
while condition():
func()
Utilizar como tal:
>>> x = 10
>>> def f():
... global x
... x = x - 1
>>> def c():
global x
return x > 0
>>> dowhile(f, c)
>>> print x
0
La excepción romperá el bucle, por lo que también podría manejarlo fuera del bucle.
try:
while True:
if s:
print s
s = i.next()
except StopIteration:
pass
Supongo que el problema con su código es que el comportamiento de break
interior, except
que no está definido. Por lo general, la break
solo sube un nivel, por lo que, por ejemplo, la break
interna try
ir directamente a finally
(si existe) un fuera del try
, pero no el bucle.
PEP relacionado: http://www.python.org/dev/peps/pep-3136
Pregunta relacionada: Romper con los bucles anidados.
La forma en que he hecho esto es la siguiente ...
condition = True
while condition:
do_stuff()
condition = (<something that evaluates to True or False>)
Esto me parece la solución simplista, me sorprende que no la haya visto aquí todavía. Esto obviamente también puede ser invertido a
while not condition:
etc.
Mi código a continuación puede ser una implementación útil, destacando la diferencia principal entre do-while y en el while que lo entiendo.
Así que en este caso, siempre pasas por el bucle al menos una vez.
first_pass = True
while first_pass or condition:
first_pass = False
do_stuff()
No estoy seguro de lo que estás tratando de hacer. Puede implementar un bucle do-while como este:
while True:
stuff()
if fail_condition:
break
O:
stuff()
while not fail_condition:
stuff()
¿Qué estás haciendo tratando de usar un bucle do while para imprimir las cosas en la lista? ¿Por qué no solo usar:
for i in l:
print i
print "done"
Actualizar:
Entonces, ¿tienes una lista de líneas? ¿Y quieres seguir iterando a través de él? Qué tal si:
for s in l:
while True:
stuff()
# use a "break" instead of s = i.next()
¿Eso parece algo cercano a lo que querrías? Con su ejemplo de código, sería:
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
Porque no solo haces
for s in l :
print s
print "done"
?
Si se encuentra en un escenario en el que está haciendo un bucle mientras un recurso no es valioso o algo similar que lanza una excepción, podría usar algo como
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
para un bucle do - while que contiene declaraciones 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()
alternativamente, cuando no hay necesidad de la cláusula ''finalmente''
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
do {
stuff()
} while (condition())
->
while True:
stuff()
if not condition():
break
Puedes hacer una función:
def do_while(stuff, condition):
while condition(stuff()):
pass
Pero 1) es feo. 2) La condición debe ser una función con un parámetro, que se supone que se rellena con cosas (es la única razón para no usar el bucle while clásico).
while condition is True:
stuff()
else:
stuff()