2012-01-11 15 views
12

he estado aprendiendo Clojure y desconcertado por la siguiente:Desconcertado: Clojure for loop with: while -> comportamiento inesperado?

user=> (for [a (range 1 4) b (range 1 4)] [a b]) 
([1 1] [1 2] [1 3] [2 1] [2 2] [2 3] [3 1] [3 2] [3 3]); _no surprise here_ 

Vamos a añadir :while (not= a b), espero ver una lista vacía como el bucle debe detenerse si la condición es falsa. En este caso, es el primer elemento donde a = b = 1. Veamos:

user=> (for [a (range 1 4) b (range 1 4) :while (not= a b) ] [a b]) 

([2 1] [3 1] [3 2]) ; _surprise!_ 

Cambio :while a :when para filtrar (= a b) pares

user=> (for [a (range 1 4) b (range 1 4) :when (not= a b) ] [a b]) 
([1 2] [1 3] [2 1] [2 3] [3 1] [3 2]); _expected_ 

podría alguien explicar por qué se comporta como (for [ ... :while ..] ...) esto?

estoy usando Clojure 1.3 en OS X.

Gracias y disculpas por la falta de formato. Esta es mi publicación virgen en StackOverflow.

Respuesta

9

Miremos cada iteración.

a = 1 
    b = 1 -> a == b, break because of while 

a = 2 
    b = 1 -> a != b, print [2 1] 
    b = 2 -> a == b, break because of while 

a = 3 
    b = 1 -> a != b, print [3 1] 
    b = 2 -> a != b, print [3 2] 
    b = 3 -> a == b, break because of while 
+0

Gracias, Nikita. Entonces, si: mientras solo se aplica al ciclo interno, ¿cómo hago que se aplique también al ciclo externo? El problema que tengo es: (para [de [: a: b: c: d: e: f] a [: a: b: c: d: e: f]: dejar [camino (buscar-camino ab)] : while path) path); el ciclo debería detenerse cuando el camino es nulo. – jbear

+0

Lo siento, me refería al (para [de [: a: b: c: d: e: f] a [: a: b: c: d: e: f]: dejar [ruta (buscar-ruta desde a)] ]: while path] path) – jbear

+0

@jbear, no sé :(Puede ser que sea mejor que cree un seq perezoso de rutas y tome mientras no sean nulas. No estoy seguro de que 'for' sea flojo. –

3

La condición :while en for solamente termina la más interior de bucle. Uso for todo el tiempo, pero :while tan raramente que nunca me di cuenta de esto; gracias por la gran pregunta!

Tristemente, creo que lo mejor que puede hacer es envolver take-while alrededor del for, ya que quiere un contador de parada "global" en la secuencia de salida, no un contador de parada en una de las secuencias de entrada que está iterando encima. Por ejemplo:

(->> (for [a (range 1 4) 
      b (range 1 4)] 
     [a b]) 
    (take-while (fn [[a b]] (not= a b)))) 

() 
+1

Para que quede claro, la prueba': while' se aplica a la secuencia inmediatamente anterior, no necesariamente al ciclo interno. –

+0

Gracias por la aclaración, Alex. Dando la prominencia del lenguaje, No pude evitar sentirme un poco decepcionado de que uno de los componentes fundamentales - la comprensión de la lista - sea más bien limitado. Afortunadamente, a medida que avance mi aprendizaje, descubriré más/mejores maneras de hacerlo, con gran ayuda de todos. – jbear

+0

@jbear En realidad es menos limitante de esta manera que ser el otro camino. De esta forma, puede envolver fácilmente una expresión 'for' en un' take-while' si desea el comportamiento "global". Pero si el comportamiento "global" fuera lo que 'for /: while' hizo, ¿cómo recuperarías el comportamiento" intermedio "actual? Tendría que abandonar 'for' por completo y construir un nido de ratas de mapa, mapcat, filtro, take-while. – amalloy