[Python-ideas] for-while statement
Masklinn
masklinn at masklinn.net
Wed Feb 19 23:07:29 CET 2014
On 2014-02-19, at 14:34 , Alejandro López Correa <alc at spika.net> wrote:
> Hello,
>
> I think adding an optional "WHILE" clause in "FOR" loops might be
> useful sometimes (shorter code and/or improved readability):
It seems judicious application of itertools can do the job.
> for #VAR# in #SEQUENCE# while #WATCHDOG_EXPRESSION#:
> #CODE_BLOCK#
>
> Examples:
>
> keepRunning = True
> for i in range(100) while keepRunning:
> keepRunning = do_some_work( i )
>
> found = False
> for candidate in sequence while not found:
> try:
> process( candidate )
> found = True
> except InvalidCandidate:
> pass
>
> retryCount = 7
> for i in range(1,1+retryCount) while resource.acquire() == FAIL:
> sleep( i**2 )
>
>
> At the moment, I usually implement this either with ugly breaks:
>
>
> for i in range(100):
> if not do_some_work( i ):
> break
for i in takewhile(do_some_work, range(100)):
pass
>
> found = False
> for candidate in sequence:
> try:
> process_candidate()
> except InvalidCandidate:
> pass
> else:
> found = True
> break
>
for candidate in sequence:
try:
process_candidate()
except InvalidCandidate:
pass
else:
# found
break
else:
# not found
>
> Or with while loops and counters (or counting-like expressions):
>
>
> i = 1
> while i <= retryCount and not resource.acquired:
> if resource.acquire() == FAIL:
> sleep( i**2 )
> i += 1
>
>
> Of course, actual code tends to be more complex, with "keepRunning"
> being modified in some branches of "IF" blocks, and there might be
> nested loops where the exit condition for the outer one is set in the
> inner loop. Compare these two examples:
>
> found = False
> for filePath in glob( '*.data' ):
> for line in open( filePath, 'rt' ):
> if line.startswith( '#' ):
> continue
> if handle( line ):
> found = True
> break
> if found:
> break
> found = False
> for filePath in glob( '*.data' ) while not found:
> for line in open( filePath, 'rt' ) while not found:
> if line.startswith( '#' ):
> continue
> if handle( line ):
> found = True
>
# itertools's missing piece
flatmap = lambda fn, *it: chain.from_iterable(imap(fn, *it))
lines = flatmap(open, iglob('*.data'), repeat('rb')
non_comments = ifilter(lambda line: not line.startswith('#'), lines)
matches = ifilter(handle, non_comments)
match = next(matches, None)
Alternatively the filters could be replaced by
matches =(line for line in lines
if not line.startswith('#')
if handle(line))
More information about the Python-ideas
mailing list