2010-08-18 10 views
7

Esto podría ser una pregunta tonta, pero:¿El compilador clojure evaluará automáticamente expresiones de literales en tiempo de compilación?

Supongamos que una expresión depende solo de literales, o en otras expresiones que también solo dependen de literales; ¿el compilador evaluará esto en tiempo de compilación?

Supongamos que tengo,

(def a (some-time-consuming-function some-literal)) 

(def b (some-other-time-consuming-function a)) 

los dos para b y a ser evaluados por completo en tiempo de compilación, por lo que el usuario no se ve afectada?

EDITAR: Muchas gracias, todas las respuestas fueron muy útiles.

EDITAR 6.6.2011: Resulta que si intenta utilizar esta técnica para calcular previamente una estructura de datos muy grande, es fácil crear archivos de clase demasiado grandes para cargar. En esos casos, desea crear un archivo que se leerá en lugar de un archivo de clase que se cargará. Los macro trucos descritos en estas respuestas solo deben aplicarse como en situaciones en las que el valor de retorno no es una estructura prohibitivamente grande.

El error arrojado es: "java.lang.ClassFormatError: este índice de clase no es válido" Consulte this thread para la discusión de una situación relacionada.

Respuesta

6

No es para nada tonto, tenía que pensarlo y probarlo.

Funcionará solo si usa macros en lugar de funciones, ya que el cuerpo de una macro se evalúa en compilación/macroexpansión-tiempo. Ej .:

 
(defmacro preprocess [f & args] 
    (let [x# (apply (resolve f) args)] 
    `~x#)) 

(def a (preprocess some-time-consuming-function some-literal)) 

(def b (preprocess some-other-time-consuming-function a)) 

Entonces a y b son def 'd que los valores devueltos desde la evaluación de preprocess.

+0

Pensé que las macros podrían ser una forma de evitar esto. Tres preguntas: 1. ¿Cómo hiciste esto? (Pensé en usar la función de Ackerman, y ver en qué punto del proceso noté una desaceleración, pero no sé cuál es la forma más rigurosa de hacerlo). 2. Cuándo se evalúa b - en el momento de la carga ? 3. ¿No se cancelan entre sí los símbolos de comillas sytax? De todos modos, muchas gracias por la respuesta! –

+0

1) Lo probé usando una métrica diferente, a saber, una función que llamó println. El mensaje aparece durante la compilación pero no aparece cuando se carga el espacio de nombres compilado, por lo tanto, se puede concluir que la función no se estaba ejecutando en tiempo de ejecución. 2) El valor asignado a var b se evalúa durante la macroexpansión. 3) Sip. Fue abandonado por error de una iteración anterior. –

+0

Para que quede claro, la respuesta a su pregunta es "no". Es solo que hay formas de lograr tal comportamiento cuando lo desee. –

3

En su ejemplo, las funciones que consumen mucho tiempo se llaman una sola vez, cuando se carga su código.

El compilador Clojure no intenta optimizar expresiones constantes, pero el compilador Java JIT puede hacerlo en algunos casos.

3

En primer lugar, hay una razón bastante importante por la cual los lados derechos de def s deben evaluarse en el momento de la carga: pueden depender del entorno de alguna manera y en el caso general es imposible saber si lo hacen o no. no. Tome, por ejemplo,

(def *available-processors* (.availableProcessors (Runtime/getRuntime))) 

En segundo lugar, aquí es un enfoque para probar lo que realmente sucede:

  1. Crear un proyecto de prueba con Leiningen - por ejemplo, lein new testdefs.

  2. Pon :main testdefs.core en project.clj.

  3. poner lo siguiente en src/testdefs/core.clj:

    (ns testdefs.core 
        (:gen-class)) 
    
    (defn take-your-time [t] 
        (printf "Taking my time (%d)...\n" t) 
        (Thread/sleep t)) 
    
    (def a (take-your-time 5000)) 
    
    (defmacro frozen-def [v e] 
        (let [val (eval e)] 
        `(def ~v ~val))) 
    
    (frozen-def b (take-your-time 5000)) 
    
    (defn -main [& args] 
        (println "Starting...") 
        (println a) 
        (println b)) 
    
  4. Run lein uberjar; seguramente, el código toma su tiempo dos veces.

  5. Ejecute java -jar testdefs-1.0.0-SNAPSHOT-standalone.jar; notarás que el código solo toma su tiempo una vez.

+0

Lo siento por el espaciado extraño en el código - parece algo de rareza SO Markdown o no tan grande Markdown-fu de mi parte (la segunda posibilidad parece más probable). –

+0

No puedo creer que olvidé que hay una función para dormir. –

0

Para los ejemplos dados, las expresiones se evalúan en tiempo de compilación/carga. defsiempre evalúa su segundo argumento. De special forms

(def symbol init?) 

Crea y pasantes o localiza una var global con el nombre de symbol y un espacio de nombres del valor del espacio de nombres actual *ns*. Si se suministra init, se evalúa, y el enlace de raíz de la var se establece en el valor resultante.

Cuestiones relacionadas