2010-04-26 13 views
5

Tengo un bucle que desea ejecutar hasta el agotamiento o hasta que se alcanza el límite especificado por el usuario. Tengo un constructo que se ve mal, pero parece que no puedo encontrar una forma más elegante de expresarlo; hay uno?¿una forma más pitónica de expresar un bucle condicionalmente limitado?

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! Tiene que haber una mejor manera. Para propósitos de un ejemplo de trabajo, se usa xrange donde normalmente tengo un iterador de longitud finita pero desconocida (y el predicado a veces devuelve False).

+0

Al menos puede comprobar si es Ninguno límite antes del bucle y volver si es así en lugar de comprobar en cualquier momento que el predicado es cierto. Eso realmente no lo hace más pitónico, pero puede ahorrar una gran cantidad de cálculos innecesarios en su ciclo. –

+0

Olvidó poner la acción real aquí, pero espero que el límite = Ninguna signifique "sin límite", no "no haga nada". –

+0

Tenga en cuenta que la limpieza más simple que puede hacer aquí es invertir la condición: 'if not predicate (i): continue', que evita poner todo el resto del bloque en un nivel de anidación adicional. Esto se aplica a una gran cantidad de código, por lo que es bueno aprender en general. –

Respuesta

11

Tal vez algo como esto sería un poco mejor:

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 
+0

+1 ¡Buena solución! –

+0

perfecto, gracias. – msw

+6

Esto se aprieta demasiado en una línea; el código original es más claro. Ayudaría mucho separar el anidamiento; 'iter = ifilter (predicate, xrange (10 ** 5))' y luego 'for i in islice (iter, limit)'. –

2

Me gustaría tener un buen vistazo a la biblioteca itertools. El uso de eso, creo que tendría algo así como ...

# 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) 
+0

+1 de hecho, ese módulo tiene algunos poderes sutiles (o poderosas sutilezas). – msw

+0

Creo que los parámetros para 'tomar' están invertidos, es decir, tienes' def take (n, iterable) ', pero lo llamas como' take (iterable, n) '. – bcat

1

me gustaría empezar con

if limit is None: return 

puesto que nada le puede suceder a limit cuando se inicia como None (si no hay efectos secundarios deseables en la iteración y en el cálculo de predicate - si los hay, entonces, en este caso, puede hacer for i in xrange(10**5): predicate(i)).

Si limit no es None, a continuación, lo que desea es realizar max(limit, 1) cálculos de predicate lo que sea verdad, por lo que un itertools.islice de un itertools.ifilter haría:

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 
+0

lo siento, simplifiqué demasiado, solo son los efectos secundarios de predicate() que necesito. También probé un límite 'if' como se sugirió pero sentí que estaba repitiendo el código en las ramas. – msw

0

Lo que se quiere hacer parece perfectamente adecuado para una mientras bucle:

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 

El bucle se detiene si cualquiera max o limit llegue a cero.

1

debe quitar la ifs anidados:

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

Um. Por lo que yo entiendo, predicate solo calcula en segmentos, e ignoras por completo su valor de retorno, ¿verdad?

Esta es otra toma:

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 

Además, eliminar la innecesaria return True desde el extremo de predicate.

Cuestiones relacionadas