可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I need to emulate a do-while loop in a Python program. Unfortunately, the following straightforward code does not work:
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\"
Instead of \"1,2,3,done\", it prints the following output:
[stdout:]1
[stdout:]2
[stdout:]3
None[\'Traceback (most recent call last):
\', \' File \"test_python.py\", line 8, in <module>
s = i.next()
\', \'StopIteration
\']
What can I do in order to catch the \'stop iteration\' exception and break a while
loop properly?
An example of why such a thing may be needed is shown below as pseudocode.
State machine:
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
回答1:
I am not sure what you are trying to do. You can implement a do-while loop like this:
while True:
stuff()
if fail_condition:
break
Or:
stuff()
while not fail_condition:
stuff()
What are you doing trying to use a do while loop to print the stuff in the list? Why not just use:
for i in l:
print i
print \"done\"
Update:
So do you have a list of lines? And you want to keep iterating through it? How about:
for s in l:
while True:
stuff()
# use a \"break\" instead of s = i.next()
Does that seem like something close to what you would want? With your code example, it would be:
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
回答2:
Here\'s a very simple way to emulate a do-while loop:
condition = True
while condition:
# loop body here
condition = test_loop_condition()
# end of loop
The key features of a do-while loop are that the loop body always executes at least once, and that the condition is evaluated at the bottom of the loop body. The control structure show here accomplishes both of these with no need for exceptions or break statements. It does introduce one extra Boolean variable.
回答3:
My code below might be a useful implementation, highlighting the main difference between do-while vs while as I understand it.
So in this one case, you always go through the loop at least once.
first_pass = True
while first_pass or condition:
first_pass = False
do_stuff()
回答4:
Exception will break the loop, so you might as well handle it outside the loop.
try:
while True:
if s:
print s
s = i.next()
except StopIteration:
pass
I guess that the problem with your code is that behaviour of break
inside except
is not defined. Generally break
goes only one level up, so e.g. break
inside try
goes directly to finally
(if it exists) an out of the try
, but not out of the loop.
Related PEP: http://www.python.org/dev/peps/pep-3136
Related question: Breaking out of nested loops
回答5:
do {
stuff()
} while (condition())
->
while True:
stuff()
if not condition():
break
You can do a function:
def do_while(stuff, condition):
while condition(stuff()):
pass
But
1) It\'s ugly.
2) Condition should be a function with one parameter, supposed to be filled by stuff (it\'s the only reason not to use the classic while loop.)
回答6:
Here is a crazier solution of a different pattern -- using coroutines. The code is still very similar, but with one important difference; there are no exit conditions at all! The coroutine (chain of coroutines really) just stops when you stop feeding it with data.
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)
The code above collects all tokens as tuples in tokens
and I assume there is no difference between .append()
and .add()
in the original code.
回答7:
for a do - while loop containing try statements
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()
alternatively, when there\'s no need for the \'finally\' clause
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
回答8:
while condition is True:
stuff()
else:
stuff()
回答9:
Quick hack:
def dowhile(func = None, condition = None):
if not func or not condition:
return
else:
func()
while condition():
func()
Use like so:
>>> x = 10
>>> def f():
... global x
... x = x - 1
>>> def c():
global x
return x > 0
>>> dowhile(f, c)
>>> print x
0
回答10:
The way I\'ve done this is as follows...
condition = True
while condition:
do_stuff()
condition = (<something that evaluates to True or False>)
This seems to me to be the simplistic solution, I\'m surprised I haven\'t seen it here already. This can obviously also be inverted to
while not condition:
etc.
回答11:
Why don\'t you just do
for s in l :
print s
print \"done\"
?
回答12:
See if this helps :
Set a flag inside the exception handler and check it before working on the s.
flagBreak = false;
while True :
if flagBreak : break
if s :
print s
try :
s = i.next()
except StopIteration :
flagBreak = true
print \"done\"
回答13:
If you\'re in a scenario where you are looping while a resource is unavaliable or something similar that throws an exception, you could use something like
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