如何模拟 do-while 循环?

How to emulate a do-while loop?

提问人:grigoryvp 提问时间:4/13/2009 最后编辑:martineaugrigoryvp 更新时间:9/27/2023 访问量:1730660

问:

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

我能做些什么来捕获“停止迭代”异常并中断一段时间 正确循环?

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

状态机:

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
python while-loop do-while while

评论

7赞 S.Lott 4/13/2009
呃......这不是一个适当的“同时”;这简直就是“永远做”。“while True”和“break”有什么问题?
102赞 Tom 4/14/2009
S. Lott:我很确定他的问题是关于如何在 python 中实现 do。所以,我不认为他的代码是完全正确的。而且,他非常接近做一段时间......他正在检查“永远”循环结束时的状况,看看他是否应该爆发。这不是“永远做”。
6赞 underrun 9/22/2011
所以。。。您的初始示例代码实际上对我有用,没有问题,而且我没有得到回溯。这是 do while 循环的正确用语,其中中断条件是迭代器耗尽。通常,你会设置而不是 None,并且可能会做一些初始工作,而不仅仅是让你的第一次循环变得无用。s=i.next()
5赞 Hannele 10/3/2012
@underrun 不幸的是,这篇文章没有标记使用哪个版本的 Python - 原始片段也适用于我使用 2.7,可能是由于对 Python 语言本身的更新。

答:

4赞 Martin 4/13/2009 #1

你为什么不直接做

for s in l :
    print s
print "done"

?

评论

1赞 grigoryvp 4/13/2009
我需要创建一个状态机。在状态机中,重新计算 CURRENT 语句是正常情况,因此我需要“继续”而不迭代下一项。我不知道如何在 'for s in l: ' 迭代:(中做这样的事情。在 do-while 循环中,“continue”将重新评估当前项目,并在结束时进行迭代。
0赞 Martin 4/13/2009
那么,你能为你的状态机定义一些伪代码吗,这样我们就可以提示你找到最好的 Python 解决方案?我对状态机了解不多(而且可能不是唯一一个),所以如果你告诉我们一些关于你的算法的信息,这将更容易帮助我们。
0赞 harry 9/19/2013
for 循环不适用于以下情况: a = fun() while a == 'zxc': sleep(10) a = fun()
0赞 WestCoastProjects 11/24/2018
这完全忽略了检查布尔条件的意义
1290赞 Tom 4/13/2009 #2

我不确定你想做什么。您可以实现如下所示的 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赞 grigoryvp 4/13/2009
我需要创建一个状态机。在状态机中,重新计算 CURRENT 语句是正常情况,因此我需要“继续”而不迭代下一项。我不知道如何在 'for s in l: ' 迭代:(中做这样的事情。在 do-while 循环中,“continue”将重新评估当前项目,最后迭代
0赞 Tom 4/13/2009
你的意思是你需要跟踪你在列表中的位置吗?这样,当您返回相同的状态时,您可以从上次中断的地方继续?提供更多的背景信息。似乎最好在列表中使用索引。
11赞 Kr0e 8/24/2013
很遗憾 python 没有 do-while 循环。Python 是 DRY,嗯?
89赞 dtk 8/15/2016
另请参阅 PEP 315 的官方立场/理由:“建议该语言的用户在适当的 do-while 循环时使用带有内部 if-break 的 while-True 形式。
1赞 egbit 4/17/2022
虽然 PEP 315 可能看起来相关,但该建议并不像大多数其他语言那样用于 do while 循环:循环语句在 while 语句之前和之后。难怪它被拒绝了。
35赞 vartec 4/13/2009 #3

异常会破坏循环,所以你不妨在循环外处理它。

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

我猜你的代码的问题是没有定义内部的行为。通常只上升一级,例如 inside 直接进入(如果存在)out of the ,但不是 out of the loop。breakexceptbreakbreaktryfinallytry

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

评论

10赞 Paggas 11/3/2009
不过,最好只在 try 语句中包含您期望抛出异常的内容,以免捕获不需要的异常。
3赞 vartec 11/11/2010
@PiPeep:没问题,只要记住,对某些语言来说是正确的,对其他语言来说可能不是正确的。Python 针对异常的密集使用进行了优化。
7赞 ncoghlan 2/18/2011
break 和 continue 在 try/except/finally 语句的任何子句中都有完美的定义。他们只是忽略它们,然后根据需要中断或继续执行包含 while 或 for 循环的下一次迭代。作为循环构造的组件,它们仅与 while 和 for 语句相关,如果它们在到达最内部的循环之前遇到 class 或 def 语句,则会触发语法错误。它们忽略 if、with 和 try 语句。
3赞 WestCoastProjects 3/21/2019
..这是一个重要的案例
1赞 Damian Yerrick 5/22/2019
@1313e 一个常见的用例是检索查询的分页结果:如果这是结果的第一页,或者上一页结果表明有更多结果要提取,则提取另一页结果。
0赞 Nrj 4/13/2009 #4

看看这是否有帮助:

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

flagBreak = false;
while True :

    if flagBreak : break

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

print "done"

评论

3赞 martineau 10/3/2012
可以通过使用和删除 .while not flagBreak:if (flagBreak) : break
3赞 IceArdor 3/12/2014
我避免使用名为 --I am unable source to 推断 True 值或 False 值的含义的变量。请改用 或 。代码将变为 .flagdoneendOfIterationwhile not done: ...
38赞 ZeD 4/13/2009 #5
do {
  stuff()
} while (condition())

->

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

你可以做一个函数:

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

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

评论

6赞 Noctis Skytower 9/12/2012
写作是个好主意。谢谢!while True: stuff(); if not condition(): break
3赞 Sergey Lossev 1/8/2018
@ZeD,为什么 1) 丑陋?没关系,恕我直言
1赞 exic 4/29/2019
@SergeyLossev 要掌握程序的逻辑会很困难,因为如果你中间有很多“东西”代码,它一开始看起来是一个无限循环。
17赞 u0b34a0f6ae 11/3/2009 #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()

评论

5赞 Noctis Skytower 9/12/2012
你今天会如何在 Python 3.x 中写这个?
393赞 powderflask 3/14/2010 #7

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

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

do-while 循环的主要特征是循环体始终至少执行一次,并且条件在循环体的底部进行计算。此处显示的控制结构可以同时实现这两项任务,而无需异常或中断语句。它确实引入了一个额外的布尔变量。

评论

17赞 martineau 10/3/2012
它并不总是添加额外的布尔变量。通常,有些东西已经存在,其状态可以被测试。
22赞 Roberto 10/9/2013
我最喜欢这个解决方案的原因是它没有添加另一个条件,它仍然只是一个周期,如果你为辅助变量取一个好名字,整个结构就很清楚了。
9赞 ToolmakerSteve 12/15/2013
注意:虽然这确实解决了最初的问题,但这种方法不如使用 .具体来说,如果有逻辑需要 AFTER ,一旦我们完成就不应该执行,它必须被包装在 .顺便说一句,含糊不清。更具描述性:或 。breaktest_loop_condition()if condition:conditionmorenotDone
10赞 nonsensickle 9/25/2015
@ToolmakerSteve我不同意。我很少在循环中使用它,当我在我维护的代码中遇到它时,我发现循环通常可以在没有它的情况下编写。IMO 提出的解决方案是用 python 表示 do while 构造的最清晰方式。break
1赞 Josiah Yoder 9/29/2015
理想情况下,条件将被命名为描述性的东西,如 or (在这种情况下,循环将开始has_no_errorsend_reachedwhile not end_reached
4赞 MuSheng 11/30/2010 #8
while condition is True: 
  stuff()
else:
  stuff()

评论

8赞 mattdm 1/26/2012
呃。这似乎比使用休息时间要丑陋得多。
5赞 Noctis Skytower 9/12/2012
这很聪明,但它需要是一个函数或代码体要重复。stuff
13赞 martineau 10/3/2012
所需要的只是因为是隐含的。while condition:is True
2赞 yo' 2/26/2014
如果依赖于 的某个内部变量,则失败,因为该变量当时没有定义。conditionstuff()
8赞 Zv_oDD 2/27/2016
逻辑不同,因为在最后一次迭代时,当条件 != True 时:它最后一次调用代码。其中,作为 Do While,先调用代码一次,然后在重新运行之前检查条件。Do While:执行一次代码块;然后检查并重新运行,这个答案:检查并重新运行;然后执行一次代码块大不同!
8赞 Mark 5/11/2011 #9

对于包含 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

评论

0赞 Asclepius 7/2/2023
在这里使用是没有道理的。try
6赞 Naftuli Kay 4/22/2013 #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
102赞 evan54 11/24/2014 #11

我下面的代码可能是一个有用的实现,突出了我所理解的 .

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

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

评论

4赞 Zv_oDD 2/27/2016
正确答案,我认为。此外,它还可以避免断裂,以便在尝试/除块中安全使用。
0赞 markhahn 12/13/2018
JIT/优化器是否避免在第一次通过后重新测试first_pass?否则,这将是一个令人讨厌的性能问题,尽管可能是次要的
7赞 Thibault D. 5/3/2019
@markhahn这真的很小,但如果你关心这些细节,你可以在循环中反转 2 个布尔值:.然后总是首先评估,总体上只评估两次(第一次和最后一次迭代)。不要忘记在循环之前初始化为您想要的任何内容。while condition or first_pass:conditionfirst_passcondition
1赞 evan54 5/4/2019
嗯,有趣的是,我实际上故意选择了相反的方式,这样就不必初始化条件,因此需要对代码进行最小的更改。也就是说,我明白你的意思
2赞 Simply Beautiful Art 12/18/2020
@AkhilNambiar 这没有问题吗?这不是第一次通过......第一次通过后。
0赞 Ajit 5/6/2018 #12

如果你所处的场景中,你在资源不可用时循环,或者类似的东西引发异常,你可以使用类似的东西

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
17赞 Gareth Lock 6/24/2018 #13

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

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

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

while not condition:

等。

评论

4赞 cslotty 11/12/2019
你说“我很惊讶我还没有在这里看到它”——但我看不出与 2010 年的 powderflask 解决方案有任何区别。这是完全一样的。(“condition = True while condition: # loop body here condition = test_loop_condition() # 循环结束”)
0赞 Love Putin Not War 3/8/2020 #14

对我来说,一个典型的 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

我可以包括一个 for..如果情况需要,也可以在 while 循环中循环,用于循环另一组条件。

-2赞 fr_andres 6/29/2020 #15

内置的 iter 函数专门用于:

for x in iter(YOUR_FN, TERM_VAL):
    ...

例如(在 Py2 和 Py3 中测试):

class Easy:
  X = 0
  @classmethod
  def com(cls):
    cls.X += 1
    return cls.X

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

如果要给出终止条件而不是值,则始终可以设置相等,并要求该相等为 。True

10赞 Jonathan Shemer 9/8/2020 #16

Python 3.8 给出了答案。

它被称为赋值表达式。从文档中:

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

评论

6赞 Susanne Oberhauser 3/14/2022
不。 身体状况首先执行身体状况,然后评估状况构造首先检查条件。有一段时间了......do 循环。dowhile
0赞 BlackJack 6/28/2022
我更愿意为此使用和:.functools.partial()iter()for block in iter(partial, file.read, 256), ""): process(block)
0赞 luke 11/20/2022
更好的解决方案是:do = True; while do: ; something(); if (do := condition()): complain();
0赞 BorjaEst 12/3/2022
这是在“while”语句中运行代码的提示示例,因此类似于 do/while。最重要的是,您可能会获得输出。我会使用它并将所需的运行一次放在由 while 评估的函数中,其余的则在之后。
1赞 martineau 3/14/2021 #17

你想知道:

我该怎么做才能捕获“停止迭代”异常并正确中断 while 循环?

您可以如下所示,并且还利用了 Python 3.8 中引入的赋值表达式功能(又名“海象运算符”):

list_of_ints = [1, 2, 3]
iterator = iter(list_of_ints)

try:
    while (element := next(iterator)):
        print(element)
except StopIteration:
    print("done")

另一种可能性(从 Python 2.6 到 3.x 都可以使用)是为内置的 next() 函数提供默认参数以避免异常:StopIteration

SENTINEL = object()  # Unique object.
list_of_ints = [1, 2, 3]
iterator = iter(list_of_ints)

while True:
    element = next(iterator, SENTINEL)
    if element is SENTINEL:
        break
    print(element)

print("done")
18赞 Danilo Matrangolo Marano 5/3/2021 #18

我相信 python 上的这种 do-while 模拟具有最接近 C 和 Java 中存在的 do-while 结构格式的语法格式。

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

评论

1赞 lenik 5/11/2021
为什么不简单地使用呢?do = <condition>
0赞 soulmachine 9/24/2021
@lenik并没有真正模拟循环do = <condition>do ... while
0赞 Danilo Matrangolo Marano 12/2/2021
@soulmachine为什么不呢?
2赞 Moritz Friedrich 12/15/2021
因为循环始终无条件地运行第一次迭代,并且只在下一次迭代之前计算条件。do ... while
8赞 nehem 3/4/2022 #19

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
0赞 dmcontador 6/7/2022 #20
while True:
    try:
        # stuff
        stuff_1()
        if some_cond:
            continue
        if other_cond:
            break
        stuff_2()
    finally:
        # condition
        if not condition:
            break
  • [x] 仅在运行后选中conditionstuff
  • [x] 不是函数调用stuff
  • [x] 不是函数调用condition
  • [x] 可以包含流量控制stuff
  • [ ] 避免检查是否调用(可以使用另一个布尔值完成)conditionstuffbreak

评论

0赞 Asclepius 7/2/2023
在这里使用是不合理的。try...finally
1赞 Trenton 7/20/2023 #21

您可以在 Python 3.8 中使用 walrus 运算符进行一些清理:

list_of_ints = [ 1, 2, None, 3 ]
iterator = iter(list_of_ints)

_sentinel = object()
while True:
    if (i := next(iterator, _sentinel)) is _sentinel:
        break

    print(i)

这在 之外没有重复的逻辑。它还处理列表中计算结果为 的值。whileFalse

1赞 coproc 9/27/2023 #22

基于 evan54 的回答

通过定义帮助程序类

class FirstAccess:
    def __init__(self):
        self._accessed = False

    def __bool__(self):
        if self._accessed:
            return False
        self._accessed = True
        return True

可以将标志的更新保存在循环体中,因为它通过访问它来更新:

first_pass = FirstAccess()
while first_pass or condition:
    do_stuff()