Pregunta Emular un ciclo do-while en Python?


Necesito emular un ciclo do-while en un programa Python. Desafortunadamente, el siguiente código directo 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 atrapar la excepción 'detener iteración' y romper un tiempo lazo 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

546
2018-04-13 06:18


origen


Respuestas:


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

703
2018-04-13 06:28



Aquí hay una forma muy simple de emular un ciclo 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 sin necesidad de excepciones o declaraciones de interrupción. Introduce una variable booleana adicional.


241
2018-03-14 00:09



Mi código a continuación podría ser una implementación útil, destacando la diferencia principal entre  vs  como yo lo entiendo

Entonces, en este caso, siempre pasas por el ciclo al menos una vez.

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

51
2017-11-23 23:37



La excepción romperá el ciclo, por lo que también podría manejarlo fuera del ciclo.

try:
  while True:
    if s:
      print s
    s = i.next()
except StopIteration:   
  pass

Supongo que el problema con su código es ese comportamiento de break dentro except no está definido. En general break solo sube un nivel, por ejemplo, break dentro try va directamente a finally (si existe) un fuera del try, pero no fuera del circuito.

PEP relacionado: http://www.python.org/dev/peps/pep-3136 
Pregunta relacionada: Rompiendo de bucles anidados


30
2018-04-13 07:06



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 debería ser una función con un parámetro, que se supone debe estar lleno de cosas (es la única razón no para usar el ciclo while clásico)


28
2018-04-13 13:57



Aquí hay una solución más loca de un patrón diferente: usar corutinas. El código sigue siendo muy similar, pero con una diferencia importante; no hay condiciones de salida en absoluto! La coroutine (cadena de corutinas en realidad) simplemente se detiene cuando deja de alimentarlo 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 supongo que no hay diferencia entre .append() y .add() en el código original.


16
2017-11-02 17:32



para un ciclo do - while que contiene instrucciones 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

10
2018-05-10 22:03



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

7
2017-11-30 01:38



Quick Hack:

def dowhile(func = None, condition = None):
    if not func or not condition:
        return
    else:
        func()
        while condition():
            func()

Úselo así:

>>> x = 10
>>> def f():
...     global x
...     x = x - 1
>>> def c():
        global x
        return x > 0
>>> dowhile(f, c)
>>> print x
0

6
2018-04-21 21:42



¿Por qué no lo haces?

for s in l :
    print s
print "done"

?


4
2018-04-13 06:23



Vea si esto ayuda:

Establezca una bandera dentro del manejador de excepciones y revíselo antes de trabajar en la s.

flagBreak = false;
while True :

    if flagBreak : break

    if s :
        print s
    try :
        s = i.next()
    except StopIteration :
        flagBreak = true

print "done"

1
2018-04-13 08:17