如何模拟实现do-while循环?

1085

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

我应该怎么做才能捕获“stop iteration”异常并正确地结束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

7
那不是一个正确的“do-while”,那只是一个“do-forever”而已。用“while True”和“break”有什么问题吗? - S.Lott
102
我相信他的问题是关于如何在Python中实现do-while,因此我不认为他的代码完全正确。此外,他非常接近实现do-while...他在“持续”循环结束时检查条件以查看是否应该退出。这不是“do-forever”。 - Tom
6
你的初始代码在我这里可以正常运行,我没有遇到任何问题,也没有出现回溯信息。这是一个使用恰当的习语来表示迭代器耗尽时的do while循环中断条件。通常情况下,你会设置 s=i.next() 而不是 None,并可能进行一些初始工作,而不仅仅是让第一次循环变得毫无意义。 - underrun
5
很遗憾,这篇帖子没有标记使用的 Python 版本,原始片段在我使用 2.7 版本时也能正常工作,可能是由于 Python 语言本身的更新所致。 - Hannele
22个回答

1291

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

5
我需要创建一个状态机。在状态机中,重新评估当前语句是常见情况,所以我需要在不迭代下一个项目的情况下“继续”执行。我不知道如何在“for s in l:”循环中实现这样的操作 :(. 在do-while循环中,“continue”将重新评估当前项,并在末尾进行迭代。 - grigoryvp
谢谢,我已经对您的伪代码进行了评论... 因为您似乎无论处于什么状态都会以相同的方式处理“//”,所以您的示例似乎有些糟糕。此外,如果您有带斜杠的字符串,这是真实的代码吗?例如:print "blah // <-- 那会给您带来麻烦吗?" - Tom
11
很遗憾Python没有do-while循环。Python确实是DRY原则的体现,啊? - Kr0e
89
请参见[PEP 315](https://www.python.org/dev/peps/pep-0315/),了解官方立场/理由:“建议使用while-True形式,并在适当情况下使用内部if-break,而不是使用do-while循环。” - dtk
1
虽然PEP 315看起来与此相关,但该提案并不是像大多数其他语言中的do while循环:循环语句既在while语句之前又在其后。难怪它被拒绝了。 - egbit
显示剩余3条评论

393

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

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

do-while循环的主要特点是循环体至少会执行一次,条件语句在循环体底部进行判断。这种控制结构可以在不需要异常或break语句的情况下实现这两个特点。然而,它会引入一个额外的布尔变量。


17
通常不需要添加额外的布尔变量。通常会有一些已经存在的东西,可以测试它们的状态。 - martineau
22
我最喜欢这个解决方案的原因是它不会增加其他条件,仍然只有一个循环,并且如果你为辅助变量选择一个好的名称,整个结构会非常清晰。 - Roberto
9
注意:虽然这个方法可以回答原问题,但相比使用 break,它的灵活性要差一些。特别是,如果在 test_loop_condition() 之后需要执行逻辑,而我们结束后不想执行它,那么必须将其包装在 if condition: 中。顺便说一下, condition 这个名称比较模糊。更具描述性的名称是 morenotDone - ToolmakerSteve
10
@ToolmakerSteve 我不同意。我很少在循环中使用break,当我在维护的代码中遇到它时,我发现这个循环很可能本来可以写得没有它。在我看来,所示解决方案是用Python表示do while结构最清晰明了的方法。 - nonsensickle
1
理想情况下,条件应该被命名为一些描述性的东西,比如has_no_errors或者end_reached(在这种情况下,循环将从while not end_reached开始)。 - Josiah Yoder
显示剩余7条评论

102

以下是我编写的代码,它可以作为一种有用的实现方式,突出了我对 的主要区别的理解。

因此,在这种情况下,您至少会通过循环一次。

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

4
我认为这是正确的答案。此外,它避免了“break”关键字,因此可以在try/except语句块中安全使用。 - Zv_oDD
JIT/优化器是否避免在第一次通过后重新测试first_pass?否则,这可能会成为一个令人烦恼的性能问题,尽管可能不是很严重。 - markhahn
7
@markhahn 这只是个小问题,但如果你在意这样的细节,你可以在循环中交换两个布尔值的位置:while condition or first_pass:。这样 condition 就总是先被计算,而且 first_pass 只会在第一次和最后一次迭代时被计算两次。别忘了在循环之前初始化 condition 为你想要的值。 - Thibault D.
1
很有趣,我实际上故意选择了另一种方式,以避免初始化条件,从而需要最少的代码更改。话虽如此,我明白你的观点。 - evan54
这里的问题是现在在每个循环中都将first_pass设置为“False”。 - Akhil Nambiar
2
@AkhilNambiar 那没问题吧?这不是第一遍...在第一遍之后。 - Simply Beautiful Art

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

->

:这是HTML中的段落标记,用于将文本分成段落并在页面上显示出来。
while True:
  stuff()
  if not condition():
    break

你可以使用一个函数:

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

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


6
while True: stuff(); if not condition(): break 是一个非常好的主意。谢谢! - Noctis Skytower
3
@ZeD,为什么1)很丑陋? 在我看来,它还好。 - Sergey Lossev
1
@SergeyLossev 如果在程序中间有很多“杂物”代码,一开始它看起来像一个无限循环,因此理解程序的逻辑将会很困难。 - exic

35

异常会打断循环,因此您最好在循环外处理它。

try:
  while True:
    if s:
      print s
    s = i.next()
except StopIteration:   
  pass
我猜你的代码问题可能是在 except 中使用 break 的行为未定义。通常情况下,break 只能跳出一层循环,例如,在 try 中使用的 break 直接跳到 finally(如果存在)并离开 try,但不会跳出循环。

相关PEP: http://www.python.org/dev/peps/pep-3136
相关问题:Breaking out of nested loops


10
在try语句中,最好只放置可能抛出异常的代码,这样可以避免捕获不必要的异常。请注意,不要改变原意。 - Paggas
3
没问题,请记住,对于某些语言适用的规则可能不适用于其他语言。Python被优化用于大量使用异常。 - vartec
7
在try/except/finally语句的任何子句中,break和continue都有很明确的定义。它们会简单地忽略这些子句,并根据需要跳出或继续下一次迭代包含的while或for循环。作为循环结构的组成部分,它们只与while和for语句相关,并且如果在到达最内层循环之前遇到类或def语句,则会触发语法错误。它们会忽略if、with和try语句。 - ncoghlan
3
这是一个重要的案例。 - WestCoastProjects
1
@1313e 一个常见的用例是检索查询的分页结果:如果这是结果的第一页,或者先前的结果页面指示还有更多结果需要获取,则获取另一页结果。 - Damian Yerrick
显示剩余7条评论

18

我相信这个Python中的do-while模拟语法格式最接近C和Java中的do-while结构格式。

do = True
while do:
    [...]
    do = <condition>

1
为什么不直接使用 do = <condition> - lenik
@lenik do = <condition>并不能真正模拟do ... while循环。 - soulmachine
@soulmachine 为什么不呢? - Danilo Matrangolo Marano
2
因为 do ... while 循环无条件地运行第一次迭代,并且仅在下一次迭代之前评估条件。 - Moritz Friedrich

17

我做这件事的方式如下...

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

在我看来,这似乎是一种简单的解决方案,我很惊讶为什么我还没有在这里看到它。这显然也可以被倒转为

while not condition:

等等。


4
你说:“我很惊讶我还没有在这里看到它”,但我没有看出与2010年powderflask的解决方案有任何区别。完全一样。(“condition = True while condition: #循环体在此处 condition = test_loop_condition() #循环结束”) - cslotty

17

这是一种不同模式的更疯狂的解决方案,使用协程。代码仍然非常相似,但有一个重要的区别; 没有任何退出条件!协程(实际上是一系列协程)只有在停止提供数据时才会停止。

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)

上述代码将所有令牌收集为元组,并假设原始代码中.append().add()之间没有区别。


5
你如何在今天的Python 3.x中编写这个? - Noctis Skytower

10

Python 3.8有答案。

它被称为“赋值表达式”。来自文档

# Loop over fixed length blocks
while (block := f.read(256)) != '':
    process(block)

6
不对。do body while condition 首先执行 body 然后评估 _condition_。而你的结构首先检查条件,这是一个 while ... do 循环。 - Susanne Oberhauser
我更喜欢使用functools.partial()iter()来进行编程:for block in iter(partial, file.read, 256), ""): process(block) - BlackJack
更好的解决方案是:do = True; while do: ; something(); if (do := condition()): complain(); - luke
这是典型的例子,你可以在"while"语句中运行代码,因此类似于do/while。另外,你可能会得到输出。我建议将需要仅运行一次的内容放在while评估的函数中,其余部分放在之后。 - BorjaEst

8

While循环:

while condition:
  print("hello")
  

Do while循环:

while True:
  print("hello")
  if not condition:
    break

此外,您可以使用任何真正的布尔值作为条件:

while 1:
  print("hello")
  if not condition:
    break

另一种变体:

check = 1
while check:
    print("hello")
    check = condition

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接