在Python中模拟do-while循环?


797

我需要在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"

代替“ 1,2,3,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
']

为了捕获“停止迭代”异常并正确中断while循环,我该怎么办?

为什么需要这种东西的一个示例在下面显示为伪代码。

状态机:

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

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

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

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

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

Answers:


984

我不确定您要做什么。您可以像这样实现一个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

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

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

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

4
令人遗憾的是python没有do-while循环。Python是DRY,是吗?
Kr0e

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

309

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

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

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


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

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

4
注意:虽然这确实解决了原始问题,但是这种方法没有使用灵活break。具体来说,如果需要AFTER之后需要test_loop_condition()立即执行的逻辑,则必须将其包装在中if condition:。顺便说一句,condition是模糊的。更具描述性:morenotDone
ToolmakerSteve

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

1
理想情况下,条件将被命名为描述性的东西,如has_no_errorsend_reached(在这种情况下,循环将开始while not end_reached
JosiahYoder,去活,除了..

74

我下面的代码可能是一个有用的实现,着重说明了两者之间的主要区别 据我了解。

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

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

2
正确答案,我认为。此外,它还避免了break,可以安全地在try / except块中使用。
Zv_oDD '16

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

2
@markhahn这确实很小,但是如果您关心这些细节,则可以在循环中转换2个布尔值:while condition or first_pass:。然后condition总是先评估,而总体first_pass只评估两次(第一次和最后一次迭代)。不要忘记condition在循环之前初始化任何内容。
pLOPeGG '19

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

33

异常会破坏循环,因此您最好在循环之外进行处理。

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

我想您的代码的问题是break内部行为except未定义。通常break,仅上移一个级别,因此,例如breaktryInside直接进入finally(如果存在的话)a try,而不是循环。

相关的PEP:http
: //www.python.org/dev/peps/pep-3136相关的问题:打破嵌套循环


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

7
@PiPeep:RTFM,搜索EAFP。
vartec

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

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

1
..这是一个重要的案例
javadba

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

->

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

您可以执行以下功能:

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

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


5
写作while True: stuff(); if not condition(): break是一个很好的主意。谢谢!
Noctis Skytower 2012年

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

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

16

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

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()在原始代码中。


4
您今天如何用Python 3.x编写此代码?
Noctis Skytower 2012年

13

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

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

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

while not condition:

等等


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

10

做-包含try语句的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

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

8
真是的 这似乎比使用中断要难得多。
mattdm 2012年

5
那很聪明,但是它必须stuff是一个函数或要重复的代码体。
Noctis Skytower 2012年

12
所需要的只是while condition:因为is True隐含。
martineau,2012年

2
如果condition依赖于的某个内部变量,则此操作将失败stuff(),因为该变量当时尚未定义。
2014年

5
逻辑不同,因为在条件!= True的最后一次迭代中,它在最后一次调用代码。作为Do While的地方,首先调用代码一次,然后在重新运行之前检查条件。做时间:执行一次块;然后检查并重新运行,这个答案:检查并重新运行;然后执行一次代码块。很大的区别!
Zv_oDD '16

7

快速破解:

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

3

你为什么不做

for s in l :
    print s
print "done"


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

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

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

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

1

看看是否有帮助:

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

flagBreak = false;
while True :

    if flagBreak : break

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

print "done"

3
可以通过使用while not flagBreak:和删除进行简化if (flagBreak) : break
martineau,2012年

1
我避免使用名为flag-我无法推断True值或False值的含义的变量。而是使用doneendOfIteration。代码变成while not done: ...
IceArdor 2014年

1

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

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

0

对我来说,典型的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,以循环另一组条件。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.