Emular um do-while loop em Python?
-
09-09-2019 - |
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
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.
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