Эмулировать цикл 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 573

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 заключается в том, что тело цикла всегда выполняется по крайней мере один раз и что условие оценивается в нижней части тела цикла. Структура управления, показанная здесь, выполняет оба из них без необходимости исключений или операторов break. Он вводит одну дополнительную булеву переменную.

мой код ниже может быть полезной реализацией, подчеркивая основное различие между do-while vs пока как я понимаю.

поэтому в этом случае вы всегда проходите через цикл хотя бы один раз.

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

исключение разорвет цикл, поэтому вы можете также обрабатывать его вне цикла.

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

быстрый Хак:

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

почему бы тебе просто не сделать

for s in l :
    print s
print "done"

?

посмотреть, если это помогает :

установите флаг внутри обработчика исключений и проверьте его перед началом работы над 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.