Эмулировать цикл do-while в Python?
мне нужно эмулировать цикл do-while в программе Python. К сожалению, следующий простой код не работает:
l = [ 1, 2, 3 ]
i = l.__iter__()
s = None
while True :
if s :
print s
try :
s = i.next()
except StopIteration :
break
print "done"
вместо "1,2,3,сделано", он печатает следующий вывод:
[stdout:]1
[stdout:]2
[stdout:]3
None['Traceback (most recent call last):
', ' File "test_python.py", line 8, in <module>
s = i.next()
', 'StopIteration
']
что я могу сделать, чтобы поймать исключение "остановить итерацию" и сломать некоторое время петля правильно?
пример того, почему такая вещь может потребоваться, показан ниже как псевдокод.
государственную машину:
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
13 ответов
Я не уверен, что вы пытаетесь сделать. Вы можете реализовать цикл do-while следующим образом:
while True:
stuff()
if fail_condition:
break
или:
stuff()
while not fail_condition:
stuff()
что вы делаете, пытаясь использовать цикл do while для печати материалов в списке? Почему бы просто не использовать:
for i in l:
print i
print "done"
обновление:
Итак, у вас есть список строк? И вы хотите продолжать повторять это? Как насчет:
for s in l:
while True:
stuff()
# use a "break" instead of s = i.next()
это похоже на то, что вы хотели бы? С вашим кодом например, это будет:
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
вот очень простой способ эмулировать цикл do-while:
condition = True
while condition:
# loop body here
condition = test_loop_condition()
# end of loop
ключевыми особенностями цикла do-while являются то, что тело цикла всегда выполняется по крайней мере один раз и что условие оценивается в нижней части тела цикла. Структура управления show здесь выполняет оба из них без необходимости исключений или операторов break. Он вводит одну дополнительную булеву переменную.
исключение разорвет цикл, поэтому вы можете также обрабатывать его вне цикла.
try:
while True:
if s:
print s
s = i.next()
except StopIteration:
pass
Я думаю, что проблема с вашим кодом заключается в том, что поведение break
внутри except
не определен. Вообще break
идет только на один уровень вверх, так, например,break
внутри try
идет прямо к finally
(если он существует) из try
, но не из петли.
связанный PEP:http://www.python.org/dev/peps/pep-3136
Связанный вопрос: выход из вложенных циклов
do {
stuff()
} while (condition())
->
while True:
stuff()
if not condition():
break
вы можете сделать функция:
def do_while(stuff, condition):
while condition(stuff()):
pass
но 1) это некрасиво. 2) условие должно быть функцией с одним параметром, которая должна быть заполнена материалом (это единственная причина не использовать классический цикл while.)
вот безумнее решение другой узор, используя сопрограммы. Код по-прежнему очень похож, но с одним важным отличием; нет никаких условий выхода вообще! В сопрограмма (цепь действительно сопрограммы) просто останавливается, когда вы прекращаете кормить его с данными.
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)
код выше собирает все токены в виде кортежей в tokens
и я предполагаю, что нет никакой разницы между .append()
и .add()
в исходном коде.
для цикла do-while, содержащего операторы 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()
альтернативно, когда нет необходимости в предложении "finally"
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
быстрый Хак:
def dowhile(func = None, condition = None):
if not func or not condition:
return
else:
func()
while condition():
func()
используйте так:
>>> x = 10
>>> def f():
... global x
... x = x - 1
>>> def c():
global x
return x > 0
>>> dowhile(f, c)
>>> print x
0
посмотрите, помогает ли это:
установите флаг внутри обработчика исключений и проверьте его перед работой над s.
flagBreak = false;
while True :
if flagBreak : break
if s :
print s
try :
s = i.next()
except StopIteration :
flagBreak = true
print "done"
Если вы находитесь в сценарии, где вы зацикливаетесь, когда ресурс недоступен или что-то подобное, что вызывает исключение, вы можете использовать что-то вроде
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
то, как я это сделал, выглядит следующим образом...
condition = True
while condition:
do_stuff()
condition = (<something that evaluates to True or False>)
Это кажется мне упрощенным решением, я удивлен, что еще не видел его здесь. Это, очевидно, также может быть перевернуто на
while not condition:
etc.