我需要在Python程序中模拟do-while循环。不幸的是,以下简单代码不起作用:

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"



[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


评论

嗯,那不是适当的“做一会儿”;那只是“永远做”。 “ while True”和“ break”有什么问题?

S. Lott:我很确定他的问题是关于如何在python中实现do的问题。因此,我不希望他的代码是完全正确的。另外,他非常接近待做...他正在“永远”循环的末尾检查条件,看是否应该休息。不是“永远做”。

所以...您的初始示例代码实际上对我来说没有问题,并且我没有得到追溯。这是do while循环的适当习惯用法,其中break条件是迭代器耗尽。通常,您将设置s = i.next()而不是设置None,并且可能会做一些初始工作,而不是仅仅使第一次循环无效。

@underrun不幸的是,该帖子未标记使用的是哪个版本的Python-原始代码段也适用于2.7,这可能是由于Python语言本身的更新所致。

#1 楼

我不确定您要做什么。您可以实现如下所示的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


评论


我需要创建一个状态机。在状态机中,重新评估CURRENT语句是正常情况,因此我需要“继续”而不迭代下一项。我不知道如何在'for s in l:'迭代中做这样的事情:(。在do-while循环中,'continue'将重新评估当前项,最后迭代

– grigoryvp
09年4月13日在6:41

您是说需要跟踪列表中的位置吗?这样,当您返回相同状态时,您可以从上次中断的地方接机吗?给更多的背景。看来使用列表中的索引可能会更好。

–汤姆
09年4月13日在6:48

谢谢,我对您的伪代码发表了评论……您的示例似乎有点糟糕,因为无论您处于什么状态,您似乎都以相同的方式处理“ //”。此外,您正在处理注释的真实代码吗?如果您的字符串带有斜线怎么办?即:打印“等等// <-这会惹您生气吗?”

–汤姆
09年4月13日在7:44

真遗憾,python没有do-while循环。 Python是DRY,是吗?

– Kr0e
13年8月24日在8:53

另请参阅PEP 315的官方立场/理由:“建议该语言的用户在适当的do-while循环时使用while-True形式,并使用内部if中断。”

–dtk
16年8月15日在12:47

#2 楼

这是一种模拟do-while循环的非常简单的方法:

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


do-while循环的主要特征是循环主体始终至少执行一次,并在循环主体的底部评估条件。此处显示的控制结构无需异常或break语句即可完成这两项任务。它确实引入了一个额外的布尔变量。

评论


它并不总是添加额外的布尔变量。通常,已经存在一些可以测试其状态的东西。

–马丁内
2012年10月2日17:32

我最喜欢此解决方案的原因是,它没有添加其他条件,它仍然只是一个循环,如果为辅助变量选择一个好名字,则整个结构非常清晰。

–罗伯托
13-10-8在21:04

注意:虽然这确实解决了原始问题,但是这种方法没有使用break灵活。具体来说,如果在AFTER test_loop_condition()之后需要逻辑,那么一旦完成就不应执行,必须将它包装在if条件中:顺便说一句,条件模糊。更具描述性:完成与否。

–ToolmakerSteve
13 Dec 15'0:30



@ToolmakerSteve我不同意。我很少使用中断循环,当我在自己维护的代码中遇到中断时,我发现循环通常可以在没有中断的情况下编写。 IMO提出的解决方案是用python表示do while构造的最清晰方法。

–不知情
15年9月24日在23:48

是。并通过重命名条件来增加更多的乐趣,以便第二行在do::D时读取

– SpeedCoder5
18年8月6日在17:38

#3 楼

我下面的代码可能是一个有用的实现,突出了我了解的do-while和while之间的主要区别。

因此,在这种情况下,您总是至少循环一次。

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


评论


正确答案,我认为。此外,它还可以避免折断,以便在try / except块中安全使用。

– Zv_oDD
16-2-26在19:41

jit / optimizer避免在第一次通过之后重新测试first_pass吗?否则,这将是一个令人烦恼的性能问题,尽管可能很小

– markhahn
18/12/12在22:17

@markhahn这确实很小,但是如果您关心这些细节,则可以在循环中插入两个布尔值:while条件或first_pass:。然后,始终先评估条件,并且仅对一次first_pass评估两次(第一次和最后一次迭代)。不要忘记在循环到任何所需的对象之前初始化条件。

– pLOPeGG
19年5月3日,12:36

HM,有趣的是,我实际上是故意选择了另一种方法,而不必初始化条件,因此对代码的更改最少。就是说我明白你的意思

–evan54
19年5月4日,0:10

这里的问题是现在在每个循环中first_pass都设置为False。

–阿希尔·南比亚(Akhil Nambiar)
12月3日,9:50

#4 楼

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


->

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


您可以执行以下功能:

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


>但是
1)很难看。
2)条件应该是一个带有一个参数的函数,应该由填充物填充(这是不使用经典while循环的唯一原因。)

评论


边写边写:stuff();如果没有condition():break是一个很好的主意。谢谢!

– Noctis Skytower
2012年9月11日20:05

@ZeD,为什么是1)丑陋?很好,恕我直言

–谢尔盖·洛塞夫(Sergey Lossev)
18年1月7日在23:24

@SergeyLossev很难理解程序的逻辑,因为如果它们之间有很多“填充”代码,那么它一开始会显示为无限循环。

– exic
19年4月29日在6:12



#5 楼

异常会中断循环,因此您最好在循环外进行处理。

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
相关问题:突破嵌套循环

评论


最好在try语句中包含您希望引发异常的内容,以免捕获不需要的异常。

– Paggas
09年11月2日在18:10

@PiPeep:RTFM,搜索EAFP。

–vartec
2010年11月4日,9:35

@PiPeep:没问题,请记住,对于某些语言来说是正确的,对于其他语言来说可能不是正确的。 Python已针对密集使用异常进行了优化。

–vartec
2010年11月10日16:30

在try / except / finally语句的任何子句中,break和continue都是定义明确的。它们只是忽略它们,而是根据需要中断或继续进行包含while或for循环的下一次迭代。作为循环结构的组成部分,它们仅与while和for语句相关,如果它们在到达最内层的循环之前遇到了class或def语句,则会触发语法错误。他们忽略if,with和try语句。

– ncoghlan
2011-2-18在6:41

..这是一个重要的例子

– StephenBoesch
19年3月21日在5:37

#6 楼

这是使用协程的不同模式的更疯狂的解决方案。代码仍然非常相似,但是有一个重要的区别。根本没有退出条件!当您停止向其提供数据时,协程(实际上是协程链)就停止了。

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()之间没有区别。

评论


您今天将如何在Python 3.x中编写此代码?

– Noctis Skytower
2012年9月11日20:07在

#7 楼

我这样做的方式如下...

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


在我看来这是一个简单的解决方案,我很惊讶我还没有看到它在这里。显然,这也可以转换为

while not condition:


等。

评论


您说“令我感到惊讶的是,我还没有在这里看到它”,但是我认为与Powderflask自2010年以来的解决方案没有任何区别。这是完全一样的。 (“条件=条件时为True:#循环体在这里,条件= test_loop_condition()#循环结束”)

–cslotty
19年11月12日在9:53



#8 楼

做一个包含try语句的do-while循环

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


#9 楼

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


评论


w这似乎比使用中断要难看得多。

–mattdm
2012年1月26日14:42

那很聪明,但是它要求东西成为函数或代码主体要重复。

– Noctis Skytower
2012年9月11日于20:08

所需要的只是条件,因为隐式为True。

–马丁内
2012年10月2日在18:15

如果条件取决于stuff()的某个内部变量,则此操作将失败,因为此时尚未定义该变量。

–吗?
2014-2-25在20:23

逻辑不同,因为在条件!= True的最后一次迭代中,它在最后一次调用代码。作为“做时”的地方,首先调用代码一次,然后在重新运行之前检查条件。做时间:执行一次块;然后检查并重新运行,此答案:检查并重新运行;然后执行一次代码块。巨大差距!

– Zv_oDD
16-2-26在19:34



#10 楼

快速破解:

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


#11 楼

Python 3.8给出了答案。
它被称为赋值表达式。来自文档:
 # Loop over fixed length blocks
while (block := f.read(256)) != '':
    process(block)
 


#12 楼

为什么不只是

for s in l :
    print s
print "done"




评论


我需要创建一个状态机。在状态机中,重新评估CURRENT语句是正常情况,因此我需要“继续”而不迭代下一项。我不知道如何在'for s in l:'迭代中做这样的事情:(。在do-while循环中,'continue'将重新评估当前项,最后进行迭代。

– grigoryvp
09年4月13日在6:26

然后,您可以为状态机定义一些伪代码,以便我们向您提示最佳的pythonic解决方案吗?我对状态机了解不多(也许不是唯一的状态机),因此,如果您向我们介绍一些有关您的算法的信息,对我们来说,这将更容易为您提供帮助。

–马丁
09年4月13日在6:48

For循环不适用于以下情况:a = fun()而a =='zxc':sleep(10)a = fun()

–哈里
2013年9月19日在7:26

这完全错过了检查布尔条件的意义

– StephenBoesch
18年11月24日在14:16

#13 楼

看看是否有帮助:

在异常处理程序中设置一个标志,并在进行处理之前检查它。

flagBreak = false;
while True :

    if flagBreak : break

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

print "done"


评论


可以通过使用while not flagBreak:并删除if(flagBreak):break来简化。

–马丁内
2012年10月2日,18:23



我避免使用名为flag的变量-我无法推断True值或False值的含义。而是使用done或endOfIteration。代码变成未完成时的代码:....

– IceArdor
2014年3月11日在20:03

#14 楼

如果您处于资源不可用或可能引发异常的类似循环的情况下,则可以使用诸如

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


#15 楼

对我来说,典型的while循环将是这样的:

xBool = True
# A counter to force a condition (eg. yCount = some integer value)

while xBool:
    # set up the condition (eg. if yCount > 0):
        (Do something)
        yCount = yCount - 1
    else:
        # (condition is not met, set xBool False)
        xBool = False


如果情况允许,我也可以在while循环中包含for..loop遍历另一组条件。

#16 楼

内置的iter函数专门用于:
for x in iter(YOUR_FN, TERM_VAL):
    ...

(在Py2和3中进行了测试):
class Easy:
  X = 0
  @classmethod
  def com(cls):
    cls.X += 1
    return cls.X

for x in iter(Easy.com, 10):
  print(">>>", x)

如果要提供一个终止条件而不是值,则始终可以设置一个等式,并要求该等式为True