2010-04-29 10 views
18

(incluso el título de esto va a causar llamas, comprendo)Simulando C-estilo para los bucles en Python

Python tomó la decisión de diseño deliberada a tienen el uso for bucle iterables explícitos, con el beneficio de forma considerable código simplificado en la mayoría de los casos.

Sin embargo, a veces es todo un dolor de construir un iterable si su caso de prueba y la función de actualización son complicados, y así me encuentro escribiendo los siguientes ciclos while:

val = START_VAL 
while <awkward/complicated test case>: 
    # do stuff 
    ... 
    val = <awkward/complicated update> 

El problema con esto es que la actualización se encuentra en la parte inferior del bloque while, lo que significa que si yo quiero tener un continue incrustado en algún lugar que tengo que:

  • uso de código duplicado para la actualización complicada/awkard, Y

  • corre el riesgo de olvidar y tener mi código bucle infinito

Podría seguir la ruta de la mano de laminación en un iterador complicada:

def complicated_iterator(val): 
    while <awkward/complicated test case>: 
     yeild val 
     val = <awkward/complicated update> 

for val in complicated_iterator(start_val): 
    if <random check>: 
     continue # no issues here 
    # do stuff 

Esto me parece muuuuchas demasiado detallado y complicado ¿Las personas en desbordamiento de pila tienen una sugerencia más simple?

Respuesta a los comentarios:

@Glenn Maynard: Sí, lo desestimó la respuesta. Es malo escribir cinco líneas si hay una manera de hacerlo en una ... especialmente en un caso que aparece todo el tiempo (el bucle es una característica común de los programas completos de Turing).

Para las personas que buscan un ejemplo concreto: digamos que estoy trabajando con una biblioteca de fechas personalizada. Mi pregunta sería entonces, ¿cómo expresar esto en Python:

for (date = start; date < end; date = calendar.next_quarter_end(date)): 
    if another_calendar.is_holiday(date): 
     continue 
    # ... do stuff... 
+0

añejo de val val actualizo e inmediatamente en la siguiente línea? y usa oldval en todas partes donde usaste val antes? –

+5

Un buen ejemplo de código sería bueno. Nunca he tenido que escribir un código tan incómodo en Python ... –

+0

¿Qué pasa con el primero? Necesitas todos esos elementos. ¿Es solo que C permite todo esto en una línea y Python no? – phkahler

Respuesta

25

Esto es lo mejor que puedo llegar a:

def cfor(first,test,update): 
    while test(first): 
     yield first 
     first = update(first) 

def example(blah): 
    print "do some stuff" 
    for i in cfor(0,lambda i:i<blah,lambda i:i+1): 
     print i 
    print "done" 

Me gustaría pitón tenía una sintaxis para expresiones clausurado.

Edit: Además, tenga en cuenta que solo tiene que definir cfor una vez (a diferencia de su función complicated_iterator).

+2

No puedo entender por qué Guido está deprimido en 'lambda', ¡es muy útil! –

+0

¡Esto es bastante útil! – YGA

+0

Para el registro, terminé simplemente agregando cfor (mismo nombre par) a nuestro árbol central y ya lo he usado en dos scripts separados. ¡Gracias! – YGA

3

Se puede usar un bloque try/finally para ejecutar la actualización:

val = START_VAL 

while <awkward/complicated test case>: 
    try: 
     # do stuff 
     continue 

    finally: 
     val = <awkward/complicated update> 

Advertencia: esto también ejecutar la instrucción de actualización si haces un break.

4

Estoy un poco confundido: tiene una expresión while complicada, y una próxima expresión complicada, pero encajan perfectamente en un C for loop? Eso no tiene sentido para mí.

Recomiendo el enfoque de iterador personalizado. Es probable que encuentre otros usos para el iterador, y encapsular la iteración es una buena práctica de todos modos.

ACTUALIZACIÓN: Usando su ejemplo, definitivamente crearía un iterador personalizado. Parece perfectamente natural para mí que un calendario sería capaz de generar una serie de fechas trimestrales:

class Calendar: 
    # ... 

    def quarters(self, start, end): 
     """Generate the quarter-start dates between `start` and `end`.""" 
     date = start 
     while date < end: 
      yield date 
      date = self.next_quarter_end(date) 


for date in calendar.quarters(start, end): 
    if another_calendar.is_holiday(date): 
     continue 
    # ... do stuff... 

Esto parece una abstracción maravilloso para su clase de calendario para ofrecer, y apuesto a que va a utilizar más de lo una vez.

+0

Hola Ned, Agregué un comentario colgando de la pregunta principal con un ejemplo de una próxima expresión "incómoda" que todavía es relativamente compacta. – YGA

+0

@YGA: He actualizado mi respuesta. –

+0

El problema es que hay todo tipo de secuencias de fechas imaginables, y de todos modos no es necesariamente claro que controlas la clase de calendario, entonces necesitas escribir un contenedor ... – YGA

4

¿Qué hay de:

date = start 

while date < end: 

    if not another_calendar.is_holiday(date): 
     # ... do stuff... 

    date = calendar.next_quarter_end(date) 

Pero si se utiliza ese particular constructo menudo, usted es mejor definir el generador de una vez y volver a usarlo como lo hizo en su pregunta.

(El hecho es que, dado que son idiomas diferentes, no es posible que todas las construcciones en C se asocien a una construcción más compacta en Python. Es como afirmar que tiene un algoritmo de compresión que funciona igual de bien en todos insumos)

+0

C's for loop es, para la mayoría de los intentos, solo azúcar sintáctica para un ciclo while (incluido el propio ciclo while de C). El código de ejemplo dado en la parte inferior de la pregunta del OP es equivalente al ciclo while que se muestra aquí, que para mí es aproximadamente tan breve y fácil de leer como el C for loop. –

+0

Principalmente estoy de acuerdo, aunque donde los dialectos C permiten 'for (int idx = 0; etc)' esto tiene la ventaja de restringir el alcance del contador al bucle solamente.Esto es irrelevante en Python donde 'for' no crea un nuevo alcance. (También tenga en cuenta que mi comentario sobre la compacidad no es un juicio de valor, es decir. No estoy diciendo que "compacto == mejor", incluso si ese parece ser el criterio del OP.) – detly

+0

Hago esto en algunos casos, pero es razonable Es común que tenga todo tipo de "casos de prueba" en el cuerpo de mi ciclo while. En cierto sentido, creo que es por eso que se inventó la construcción "continuar"; porque incrustar toda esta lógica en las declaraciones if en la parte superior del ciclo es muy incómodo ... – YGA

2

menudo hago

while True: 
    val = <awkward/complicated update> 
    if not val: 
    break 

    etc. 
0

Je:.

def forre(a,i,c,top,increment,run): 
    increment = increment.replace("++","+=1").replace("--","-=1").replace("; ","") 
    while i != top: 
     try: exec(run) 
     except: print "error: "; print run 
     try: exec(increment) 
     except: print "error: "; print increment 

forre("int i=",0,"; i<",6,"; i++", 
    "print i" 
    )