Hamza Yerlikaya ha già fatto il punto più importante, ovvero che il codice Clojure è sempre compilato. Sto solo aggiungendo un'illustrazione e alcune informazioni su alcuni frutti a basso impatto per i tuoi sforzi di ottimizzazione.
In primo luogo, il punto di cui sopra sul codice di Clojure sempre in fase di compilazione comprende chiusure restituiti dalle funzioni e funzioni create di ordine superiore chiamando eval
su fn
/fn*
forme e in effetti qualsiasi altra cosa che può agire come una funzione Clojure. Quindi non hai bisogno di un modem DSL separato per descrivere le funzioni, basta usare le funzioni di ordine superiore (e possibilmente macro):
(defn make-affine-function [a b]
(fn [x] (+ (* a x) b)))
((make-affine-function 31 47) 5)
; => 202
cose sarebbe più interessante se le caratteristiche del tuo dovessero includere le informazioni sui tipi di parametri, come quindi potresti essere interessato a scrivere una macro per generare codice usando quei suggerimenti tipo. L'esempio più semplice che posso pensare sarebbe una variante di quanto sopra:
(defmacro make-primitive-affine-function [t a b]
(let [cast #(list (symbol (name t)) %)
x (gensym "x")]
`(fn [~x] (+ (* ~(cast a) ~(cast x)) ~(cast b)))))
((make-primitive-affine-function :int 31 47) 5)
; => 202
Uso :int
, :long
, :float
o :double
(oi simboli non namespace qualificato di nomi corrispondenti) come primo parametro di sfruttare di aritmetica primitiva unboxed appropriata per i tuoi tipi di argomento. A seconda di cosa sta facendo la tua funzione, questo potrebbe darti un notevole incremento delle prestazioni.
Altri tipi di suggerimenti sono normalmente forniti con la sintassi #^Foo bar
(^Foo bar
fa la stessa cosa in 1.2); se vuoi aggiungerli al codice generato da una macro, investiga la funzione with-meta
(dovrai unire '{:tag Foo}
nei metadati dei simboli che rappresentano gli argomenti formali alle tue funzioni o let
-introdotti i locals a cui vuoi mettere suggerimenti di tipo).
Oh, e nel caso in cui si desidera ancora di sapere come implementare la vostra idea originale ...
È sempre possibile costruire l'espressione Clojure per definire la vostra funzione - (list 'fn ['x] (a-magic-function-to-generate-some-code some-args ...))
- e la chiamata eval
sul risultato.Ciò ti consentirebbe di fare qualcosa di simile al seguente (sarebbe più semplice richiedere che la specifica includesse l'elenco dei parametri, ma qui è una versione che presuppone che gli argomenti debbano essere pescati dalle specifiche, tutti sono chiamati paramFOO
e devono essere ordinati lessicograficamente):
(require '[clojure.walk :as walk])
(defn compile-spec [spec]
(let [params (atom #{})]
(walk/prewalk
(fn [item]
(if (and (symbol? item) (.startsWith (name item) "param"))
(do (swap! params conj item)
item)
item))
spec)
(eval `(fn [[email protected](sort @params)] [email protected]))))
(def my-spec '[(+ (* 31 param0) 47)])
((compile-spec my-spec) 5)
; => 202
Nella stragrande maggioranza del tempo, non vi è alcuna buona ragione per fare le cose in questo modo e dovrebbe essere evitato; utilizzare invece funzioni e macro di ordine superiore. Tuttavia, se stai facendo qualcosa come, ad esempio, la programmazione evolutiva, allora è lì, fornendo la massima flessibilità - e il risultato è ancora una funzione compilata.
Questa è una grande risposta: mi ha aiutato a capire cosa sta succedendo sotto il cofano e risolve il problema perfettamente. Mille grazie Michal! – mikera
Felice di aiutare. :-) –