Simulating C-style for loops in python [duplicate]
(even the title of this is going to cause flames, I realize)
Python made the deliberate design choice to have the for
loop use explicit iterables, with the benefit of considerably simplified code in most cases.
However, sometimes it is quite a pain to construct an iterable if your test case and update function are complicated, and so I find myself writing the following while loops:
val = START_VAL
while <awkward/complicated test case>:
# do stuff
...
val = <awkward/complicated update>
The problem with this is that the update is at the bottom of the while
block, meaning that if I want to have a continue
embedded somewhere in it I have to:
use duplicate code for the complicated/awkard update, AND
run the risk of forgetting it and having my code infinite loop
I could go the route of hand-rolling a complicated iterator:
def complicated_iterator(val):
while <awkward/complicated test case>:
yeild val
val = <awkward/complicated update>
for val in complicated_iterator(start_val):
if <random check>:
continue # no issues here
# do stuff
This strikes me as waaaaay too verbose and complicated. Do folks in stack overflow have a simpler suggestion?
Response to comments:
@Glenn Maynard: Yes, I dismissed the answer. It's bad to write five lines if there is a way to do it in one... especially in a case that comes up all the time (looping being a common feature of Turing-complete programs).
For the folks looking for a concrete example: let's say I'm working with a custom date library. My question would then be, how would you express this in python:
for (date = start; date < end; date = calen开发者_如何学Godar.next_quarter_end(date)):
if another_calendar.is_holiday(date):
continue
# ... do stuff...
This is the best I can come up with:
def cfor(first,test,update):
while test(first):
yield first
first = update(first)
def example(blah):
print "do some stuff"
for i in cfor(0,lambda i:i<blah,lambda i:i+1):
print i
print "done"
I wish python had a syntax for closured expressions.
Edit: Also, note that you only have to define cfor once (as opposed to your complicated_iterator
function).
I'm a little confused: you have a complicated while expression, and a complicated next expression, but they fit nicely into a C for loop? That doesn't make sense to me.
I recommend the custom iterator approach. You will likely find other uses for the iterator, and encapsulating the iteration is good practice anyway.
UPDATE: Using your example, I would definitely make a custom iterator. It seems perfectly natural to me that a calendar would be able to generate a series of quarterly dates:
class Calendar:
# ...
def quarters(self, start, end):
"""Generate the quarter-start dates between `start` and `end`."""
date = start
while date < end:
yield date
date = self.next_quarter_end(date)
for date in calendar.quarters(start, end):
if another_calendar.is_holiday(date):
continue
# ... do stuff...
This seems like a wonderful abstraction for your calendar class to provide, and I bet you'll use it more than once.
What about:
date = start
while date < end:
if not another_calendar.is_holiday(date):
# ... do stuff...
date = calendar.next_quarter_end(date)
But if you use that particular construct often, you're better off defining the generator once and re-using it as you did in your question.
(The fact is, since they're different languages, you can't possibly have every construct in C map to a more compact construct in Python. It's like claiming to have a compression algorithm that works equally well on all random inputs.)
You could use a try/finally clause to execute the update:
val = START_VAL
while <awkward/complicated test case>:
try:
# do stuff
continue
finally:
val = <awkward/complicated update>
Caveat: this will also execute the update statement if you do a break
.
I often do
while True:
val = <awkward/complicated update>
if not val:
break
etc.
Heh:
def forre(a,i,c,top,increment,run):
increment = increment.replace("++","+=1").replace("--","-=1").replace("; ","")
while i != top:
try: exec(run)
except: print "error: "; print run
try: exec(increment)
except: print "error: "; print increment
forre("int i=",0,"; i<",6,"; i++",
"print i"
)
精彩评论