Pergunta

Eu preciso para emular um do-while loop em um programa Python. Infelizmente, o seguinte código simples não 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"

Em vez de "1,2,3, feito", ele imprime a seguinte saída:

[stdout:]1
[stdout:]2
[stdout:]3
None['Traceback (most recent call last):
', '  File "test_python.py", line 8, in <module>
    s = i.next()
', 'StopIteration
']

O que posso fazer, a fim de capturar a exceção 'stop iteração' e quebrar um tempo laço corretamente?

Um exemplo de por que tal coisa pode ser necessária é mostrado abaixo como pseudocódigo.

máquina do Estado:

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
Foi útil?

Solução

Eu não tenho certeza o que você está tentando fazer. Você pode implementar um do-while loop como este:

while True:
  stuff()
  if fail_condition:
    break

Ou:

stuff()
while not fail_condition:
  stuff()

O que você está fazendo tentando usar um loop Do While para imprimir o material na lista? Por que não usar:

for i in l:
  print i
print "done"

Update:

Então você tem uma lista de linhas? E você quiser manter a iteração através dele? Que tal:

for s in l: 
  while True: 
    stuff() 
    # use a "break" instead of s = i.next()

faz isso parecer algo perto do que você gostaria? Com o seu exemplo de código, seria:

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

Outras dicas

Aqui está uma maneira muito simples para emular um do-while loop:

condition = True
while condition:
    # loop body here
    condition = test_loop_condition()
# end of loop

As principais características de um do-while são de que o corpo do laço é executado pelo menos uma vez, e que a condição é avaliada na parte inferior do corpo do laço. O show estrutura de controle aqui realiza ambos sem a necessidade de exceções ou instruções break. Ele introduz uma variável booleana extra.

Meu código abaixo pode ser uma implementação útil, destacando a diferença principal entre vs como eu o entendo.

Assim, em este caso, você sempre ir através do loop pelo menos uma vez.

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

Exceção vai quebrar o loop, então você pode muito bem lidar com isso fora do loop.

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

Eu acho que o problema com o seu código é que o comportamento de break dentro except não está definido. Geralmente break vai apenas um nível acima, de modo que, por exemplo, break dentro try vai directamente para finally (se existir) um para fora do try, mas não fora do circuito.

PEP relacionadas: http://www.python.org/dev/peps/pep -3136
questão relacionada: Romper com nested loops de

do {
  stuff()
} while (condition())

->

while True:
  stuff()
  if not condition():
    break

Você pode fazer uma função:

def do_while(stuff, condition):
  while condition(stuff()):
    pass

Mas 1) É feio. 2) Condição deve ser uma função com um parâmetro, deveria ser preenchido por coisas (que é a única razão pela não usar o clássico, enquanto loop.)

Aqui está uma solução mais louco de um padrão diferente - usando co-rotinas. O código ainda é muito semelhante, mas com uma diferença importante; não há condições de saída em tudo! O co-rotina (cadeia de coroutines realmente) só pára quando você parar de alimentá-lo com dados.

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)

O código acima recolhe todos os tokens como tuplas em tokens e presumo que não há diferença entre .append() e .add() no código original.

A maneira que eu tenho feito isso é o seguinte ...

condition = True
while condition:
     do_stuff()
     condition = (<something that evaluates to True or False>)

Isto parece-me ser a solução simplista, eu estou surpreso que eu não vi isso aqui já. Isso pode, obviamente, também ser invertido para

while not condition:

etc.

para uma do - while contendo declarações tente

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

Como alternativa, quando não há nenhuma necessidade para a 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()

Use assim:

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

Por que você não apenas fazer

for s in l :
    print s
print "done"

?

Veja se isso ajuda:

Definir uma bandeira dentro do manipulador de exceção e verificá-lo antes de trabalhar no s.

flagBreak = false;
while True :

    if flagBreak : break

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

print "done"

Se você estiver em um cenário onde você está looping enquanto um recurso é unavaliable ou algo semelhante que lança uma exceção, você poderia 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
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top