2010-08-18 9 views
7

Questa potrebbe essere una domanda stupida, ma:Il compilatore clojure valuterà automaticamente le espressioni di letterali al momento della compilazione?

Supponiamo che un'espressione dipenda solo da valori letterali o da altre espressioni che dipendono solo da valori letterali; il compilatore lo valuterà al momento della compilazione?

Supponiamo che io sono,

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

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

saranno entrambi b ed un essere valutati completamente al momento della compilazione, in modo che l'utente non è interessato?

EDIT: Grazie mille, tutte le risposte sono state molto utili.

EDIT 6.6.2011: Si scopre che se si tenta di utilizzare questa tecnica per precalutare una struttura di dati molto ampia, è facile creare file di classe troppo grandi per essere caricati. In questi casi, si desidera creare un file che verrà letto anziché un file di classe che verrà caricato. I trucchi macro descritti in queste risposte dovrebbero essere applicati solo come è in situazioni in cui il valore di ritorno non è una struttura proibitivamente grande.

L'errore generato è: "java.lang.ClassFormatError: non valido questo indice di classe" Vedere this thread per la discussione di una situazione correlata.

risposta

6

Per niente stupido, ho dovuto pensarci e provarlo.

Funzionerà solo se si utilizzano macro anziché funzioni, poiché il corpo di una macro viene valutato in fase di compilazione/macro-espansione. Es .:

 
(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)) 

Poi a e b sono def 'd ai valori restituiti dalla valutazione preprocess.

+0

Ho pensato che le macro potrebbero essere un modo per aggirare questo. Tre domande: 1. Come hai fatto a testare questo? (Ho pensato di usare la funzione Ackerman, e vedendo a che punto del processo ho notato un rallentamento, ma non so quale sia il modo più rigoroso per farlo.) 2. Quando viene valutato b - al momento del caricamento ? 3. Non si sopprimono le virgolette e le virgolette cancellate l'una dall'altra? Comunque, grazie mille per la risposta! –

+0

1) L'ho provato usando una metrica diversa, vale a dire una funzione chiamata println. Il messaggio appare durante la compilazione ma non appare quando si carica lo spazio dei nomi compilato, quindi si può concludere che la funzione non è stata eseguita in fase di runtime. 2) Il valore assegnato alla variabile b viene valutato durante la macroespansione. 3) Sì. E 'stato erroneamente lasciato da una precedente iterazione. –

+0

Per essere chiari, la risposta alla tua domanda è "no". È solo che ci sono modi per ottenere questo comportamento quando vuoi. –

3

Nell'esempio, le funzioni che richiedono tempo sono chiamate una sola volta, quando viene caricato il codice.

Il compilatore Clojure non tenta di ottimizzare le espressioni costanti, ma il compilatore JIT Java può farlo in alcuni casi.

3

In primo luogo, c'è una ragione molto importante per cui i lati destra di def s devono essere valutati al momento del caricamento: potrebbero dipendere dall'ambiente in qualche modo e nel caso generale è impossibile dire se lo fanno o non. Prendi ad es.

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

In secondo luogo, qui è un approccio per testare ciò che effettivamente accade:

  1. Creare un progetto di test con Leiningen - per esempio, lein new testdefs.

  2. Put :main testdefs.core in project.clj.

  3. inserire il seguente nel 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; sicuramente, il codice prende il suo tempo due volte.

  5. Corsa java -jar testdefs-1.0.0-SNAPSHOT-standalone.jar; noterai che il codice impiega solo una volta il suo tempo.

+0

Ci scusiamo per la strana spaziatura nel codice - sembra una certa stranezza di SO Markdown o non così eccezionale Markdown-fu da parte mia (la seconda possibilità sembra più probabile). –

+0

Non posso credere di aver dimenticato che c'è una funzione di sonno. –

0

Per gli esempi forniti, le espressioni vengono valutate in fase di compilazione/caricamento. defsempre valuta il suo secondo argomento. Da special forms

(def symbol init?) 

crea e stagisti o individua una var globale con il nome di symbol e uno spazio dei nomi del valore dello spazio dei nomi corrente *ns*. Se viene fornito init, viene valutato e il binding radice della var viene impostato sul valore risultante.

Problemi correlati