0

What's the Pythonic (2.7, 3) way to write a for-loop with a condition for stopping early?

For example

for item in lst:
    if not condition(item):
        break
    process(item)
5
  • I'm not looking to solve a specific problem. Commented Apr 13, 2014 at 7:49
  • Well that's not terribly helpful. The correct approach depends on things like whether you want to process all items <= k or until you reach the first one. Commented Apr 13, 2014 at 7:51
  • Original problem was finding the length of the common prefix of two strings. You can use os.path.commonprefix, but it made me wonder about ways of halting a for loop early. Commented Apr 13, 2014 at 7:51
  • The code up there isn't buggy (in that way), if that's what you're asking Commented Apr 13, 2014 at 7:52
  • I see; I have edited my answer to demonstrate using takewhile from itertools for that purpose. Commented Apr 13, 2014 at 8:01

4 Answers 4

2

Your way looks great! I would normally write:

for i in lst:
    if i > k:
        break
    process(i)

(no need for explicit indexing). Or if you're feeling peckish:

map(process, it.takewhile(lambda i: i < k, lst))
Sign up to request clarification or add additional context in comments.

1 Comment

The filter way doesn't stop on first failure.
2

You can use itertools.takewhile:

from itertools import takewhile

for item in takewhile(condition, lst):
    process(item)

From the documentation:

itertools.takewhile(predicate, iterable)

Make an iterator that returns elements from the iterable as long as the predicate is true.


To find your common prefix, for example:

def common_prefix(s1, s2):
    return "".join(a for a, _ in takewhile(lambda t: t[0] == t[1], zip(s1, s2)))

7 Comments

Looks good, but seems you are missing your [] on the list comprehension.
In a functional mood, I might prefer return s1[:len(list(itertools.takewhile(operator.eq, s1, s2)))], though takewhile unfortunately doesn't take multiple iterables. I'll have to read up on argument expansion.
@Victory, that's a generator expression. Don't need parens for a genexp if it's the only parameter.
@leewangzhong - interesting, then why does print ' '.join(a for a in xrange(3) if a < 4) throw a TypeError ?
I can't see how the lambda takes two parameters. Should it be lambda (a,b):, and lambda pair: pair[0] == pair[1] in Python 3?
|
0

Default answer: the way it's already written.

for item in lst:
    if not condition(item):
        break
    process(item)

4 Comments

It is not pythonic to iterate over (what I assume is) n = len(lst); iterate over lst directly or enumerate it if you actually need the index.
n may have been precalculated as a bound. I didn't mean for the question to be about an actual list, though that's my fault.
Then you could do: for index, item in enumerate(whatever): if index > bound or condition(item): break; process(item). This won't break if len(whatever) < bound.
I know. I was trying to make a simple example. I've simplified the example and updated the question.
0

There have been attempts to establish a new syntax for list comprehension that incorporates the idea of takewhile.

In that format, the code can be written as [terrible way of list comprehension as one-line iteration without value, which will not be given in the answer itself for fear of collateral damage in impressionable minds].

For the problem of "common prefix of two strings", we have something like this:

def commonprefix(s1, s2):
    return ''.join(x for x, y in zip(s1, s2) while x==y)

Unfortunately, it seems that these attempts haven't been able to get anything agreeable: http://stupidpythonideas.blogspot.com/2013/07/syntactic-takewhile.html

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.