如何模拟实现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个回答

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

这里使用try是没有道理的。 - Asclepius

6

快速技巧:

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

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

8
哎呀,这看起来比使用断句明显更丑陋。 - mattdm
5
这很聪明,但它需要stuff是一个函数或者需要重复代码体。 - Noctis Skytower
13
只需写 while condition:,因为它已经暗含了条件是 True。 - martineau
2
如果condition取决于stuff()的某些内部变量,那么此方法将失败,因为在此时该变量未被定义。 - yo'
8
不同的逻辑,因为在最后一次迭代时,当条件不等于True时:它会再次调用代码。而Do While先执行一次代码,然后在重新运行之前检查条件。Do While:首先执行代码块;然后检查并重新运行,而本答案:检查并重新运行;然后执行代码块。这是一个很大的区别! - Zv_oDD

4

为什么不直接执行

for s in l :
    print s
print "done"

?


1
我需要创建一个状态机。在状态机中,重新评估当前语句是一种正常情况,因此我需要在不迭代下一个项目的情况下“继续”。我不知道如何在“for s in l:”迭代中做到这一点:(。在do-while循环中,“continue”将在末尾重新评估当前项目。 - grigoryvp
那么,你能定义一些伪代码来描述你的状态机,这样我们就可以向你提示最佳的Python解决方案了吗?我对状态机不是很了解(可能不是唯一一个),所以如果你告诉我们一些关于你的算法的信息,这将更容易帮助我们。 - Martin
for循环不适用于以下情况:a = fun() while a == 'zxc': sleep(10) a = fun() - harry
这完全忽略了检查布尔条件的要点。 - WestCoastProjects

1
你想知道:
如何捕获“停止迭代”异常并正确中断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()提供一个default参数,以避免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")

1
在Python 3.8中,你可以使用海象运算符来简化一些操作。
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)

这段代码在while循环之外没有重复的逻辑。它还处理了列表中求值为False的值。

1
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()

0
如果您遇到一个场景,在资源不可用或类似会抛出异常的情况下需要循环,您可以使用类似以下的方法:
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 True:
    try:
        # stuff
        stuff_1()
        if some_cond:
            continue
        if other_cond:
            break
        stuff_2()
    finally:
        # condition
        if not condition:
            break
  • [x] 运行stuff后才检查condition
  • [x] stuff不是函数调用
  • [x] condition不是函数调用
  • [x] stuff可以包含流程控制
  • [ ] 如果stuff调用了break,避免检查condition(可以使用另一个布尔值来完成)

这里不合适使用try...finally - Asclepius

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,用于循环遍历另一组条件。


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