On 2014-02-19, at 14:34 , Alejandro López Correa <alc@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))