2012-07-23 16 views
6

¿Por qué este trozo de código Clojure:Clojure constantemente mapa funcional y

user=> (map (constantly (println "Loop it.")) (range 0 3)) 

esta salida Rendimiento:

Loop it. 
(nil nil nil) 

yo esperaría que imprima "bucle" tres veces como un lado efecto de evaluar la función tres veces.

Respuesta

9

constantly no evalúa su argumento varias veces. Es una función, no una macro, por lo que el argumento se evalúa exactamente una vez antes de ejecutar constantly. Todo constantly hace es toma su argumento (evaluado) y devuelve una función que devuelve el valor dado cada vez que se llama (sin volver a evaluar nada ya que, como ya he dicho, el argumento ya se evaluó antes de que se ejecute constantly).

Si todo lo que quiere hacer es llamar al (println "Loop it") para cada elemento en el rango, debe pasar eso como la función para asignar en lugar de constantly. Tenga en cuenta que en realidad tendrá que pasarlo como una función, no como una expresión evaluada.

+0

Echó un vistazo a la fuente. Esto se ve como un ganador. – Mike

+0

Estaba intentando usar constantemente para evitar pasar explícitamente un argumento que no necesito. Sin embargo, se conformará con esto. – Mike

+10

Tenga en cuenta que si solo desea efectos secundarios, debe usar 'doseq' o' dotimes'. Dado que 'map' es flojo, no obtendrás el resultado que deseas a menos que lo fuerces con' doall' o 'dorun'. –

3

Puede obtener un comportamiento cercano a su intención usando usig repeatedly y una expresión lambda.

Por ejemplo:

(repeatedly 3 #(println "Loop it")) 

A menos que estés en la REPL, esto tiene que estar rodeado de un dorun o similar. repeatedly es flojo.

+0

Lambda no compilará porque quiere usar el argumento que le paso. En este caso, preferiría simplemente ignorar el argumento. – Mike

+0

He agregado un ejemplo de uso, análogo al tuyo. – sortega

+0

Oh, eso tiene mucho más sentido. ¡Gracias! – Mike

3

Como sepp2k correctamente señala constantly es una función, por lo que su argumento solo se evaluará una vez.

La forma idiomática para lograr lo que está haciendo aquí sería utilizar doseq:

(doseq [i (range 0 3)] 
    (println "Loop it.")) 

O, alternativamente dotimes (que es un poco más concisa y eficiente en este caso particular, ya que no está utilizando realmente la secuencia producida por range):

(dotimes [i 3] 
    (println "Loop it.")) 

Ambas soluciones no son perezosos, que es probablemente lo que quiere si usted está ejecutando algún código de los efectos secundarios.

Cuestiones relacionadas