Hamza Yerlikaya a déjà fait le point le plus important, à savoir que le code Clojure est toujours compilé. Je suis juste ajouter une illustration et quelques informations sur certains fruits mûrs pour vos efforts d'optimisation.
Tout d'abord, le point ci-dessus à propos du code de Clojure toujours en cours de compilation comprend des fermetures retournées par des fonctions d'ordre supérieur et les fonctions créées en appelant eval
sur fn
/fn*
formes et fait tout ce qui peut agir en fonction Clojure. Ainsi, vous n'avez pas besoin d'un DSL séparé pour décrire les fonctions, il suffit d'utiliser les fonctions d'ordre supérieur (et peut-être des macros):
(defn make-affine-function [a b]
(fn [x] (+ (* a x) b)))
((make-affine-function 31 47) 5)
; => 202
choses serait plus intéressant si vos spécifications devaient inclure des informations sur les types de paramètres, comme alors vous pourriez être intéressé par l'écriture d'une macro pour générer du code en utilisant ces conseils de type. L'exemple le plus simple que je peux penser serait une variante de ce qui précède:
(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
Utilisez :int
, :long
, :float
ou :double
(ou les symboles non-espace de nom qualifié de noms correspondants) comme premier argument pour tirer profit d'arithmétique primitive non mise en boîte appropriée pour vos types d'argument. En fonction de ce que fait votre fonction, cela peut vous donner un coup de pouce très significatif.
D'autres types de conseils sont normalement fournis avec la syntaxe #^Foo bar
(^Foo bar
fait la même chose en 1.2); si vous voulez les ajouter au code de macro généré, enquêter sur la fonction with-meta
(vous aurez besoin de fusionner '{:tag Foo}
dans les métadonnées des symboles représentant les arguments formels à vos fonctions ou let
habitants -introduced que vous souhaitez mettre des notes de type sur).
Oh, et si vous souhaitez toujours savoir comment mettre en œuvre votre idée originale ...
Vous pouvez toujours construire l'expression Clojure pour définir votre fonction - (list 'fn ['x] (a-magic-function-to-generate-some-code some-args ...))
- et appel eval
sur le résultat.Cela vous permettrait de faire quelque chose comme le suivant (il serait plus simple d'exiger que la spécification inclue la liste des paramètres, mais voici une version supposant que les arguments doivent être extraits de la spécification, sont tous appelés paramFOO
et doivent être triés lexicographiquement):
(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
la grande majorité du temps, il n'y a aucune raison de faire les choses de cette façon et il devrait être évité; utiliser des fonctions et des macros d'ordre supérieur à la place. Cependant, si vous faites quelque chose comme, par exemple, la programmation évolutive, alors elle est là, offrant la flexibilité ultime - et le résultat est toujours une fonction compilée.
Ceci est une excellente réponse: m'a aidé à comprendre ce qui se passe sous le capot et résout parfaitement le problème. Merci beaucoup Michal! – mikera
Heureux de vous aider. :-) –