A more pythonic way to express a conditionally restricted loop?

I have a loop that I want to run to exhaustion or until a certain user limit is reached. I have a design that looks bad, but I cannot find a more elegant way to express it; is there one?

def ello_bruce(limit=None):
    for i in xrange(10**5):
        if predicate(i):
            if not limit is None:
                limit -= 1
                if limit <= 0:
                   break

def predicate(i):
    # lengthy computation
    return True

Holy nesting! There must be a better way. For a working example, it is used xrangewhere I usually have an iterator of finite but unknown length (and the predicate sometimes returns False).

+5
source share
6 answers

Perhaps something like this would be a little better:

from itertools import ifilter, islice

def ello_bruce(limit=None):
    for i in islice(ifilter(predicate, xrange(10**5)), limit):
        # do whatever you want with i here
+11
source

itertools. , , - ...

# From the itertools examples
def tabulate(function, start=0):
    return imap(function, count(start))
def take(n, iterable):
    return list(islice(iterable, n))

# Then something like:
def ello_bruce(limit=None):
  take(filter(tabulate(predicate)), limit)
+2

if limit is None: return

limit, None ( predicate - , for i in xrange(10**5): predicate(i)).

limit None, max(limit, 1) predicate, , itertools.islice itertools.ifilter :

import itertools as it

def ello_bruce(limit=None):
    if limit is None:
        for i in xrange(10**5): predicate(i)
    else:
        for _ in it.islice(
          it.ifilter(predicate, xrange(10**5),
          max(limit, 1)): pass
+1

ifs:

if predicate(i) and not limit is None:
    ...
+1

, , while:

def ello_bruce(limit=None):
    max = 10**5
    # if you consider 0 to be an invalid value for limit you can also do
    # if limit:
    if limit is None: 
        limit = max

    while max and limit:
        if predicate(i):
            limit -= 1
        max -=1

, max, limit .

0

Um. As far as I understand, it predicatejust calculates in segments, and you completely ignore its return value, right?

This is another take:

import itertools

def ello_bruce(limit=None):
    if limit is None:
        limiter= itertools.repeat(None)
    else:
        limiter= xrange(limit)

    # since predicate is a Python function
    # itertools looping won't be faster, so use plain for.
    # remember to replace the xrange(100000) with your own iterator
    for dummy in itertools.izip(xrange(100000), limiter):
        pass

Also remove unnecessary return Truefrom the end predicate.

0
source

All Articles