¿Emular un bucle do-while en Python?

Necesito emular un bucle do-while en un progtwig de Python. Desafortunadamente, el siguiente código simple no funciona:

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" 

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

No estoy seguro de lo que estás tratando de hacer. Puedes 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 

Aquí hay una forma 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 objectives sin necesidad de excepciones ni declaraciones de interrupción. Se introduce una variable booleana extra.

Mi código a continuación puede ser una implementación útil, destacando la diferencia principal entre hacer en un momento y en el momento en 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() 

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 dentro, except que no está definido. Por lo general, la break solo sube un nivel, por lo que, por ejemplo, la break dentro del try va 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 nesteds.

 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 llena con cosas (es la única razón para no usar el bucle while clásico).

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 realmente) 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 recostack todos los tokens como tuplas en tokens y asumo que no hay diferencia entre .append() y .add() en el código original.

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

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 forma en que he hecho esto es la siguiente …

 condition = True while condition: do_stuff() condition = () 

Me parece que esta es la solución simplista, me sorprende que no la haya visto aquí todavía. Obviamente, esto también puede ser invertido a

 while not condition: 

etc.

Porque no solo haces

 for s in l : print s print "done" 

?

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" 

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