Hamza Yerlikaya hat bereits den wichtigsten Punkt gemacht, der Clojure Code ist immer kompiliert. Ich füge nur eine Illustration und einige Informationen über einige niedrig hängenden Früchte für Ihre Optimierungsbemühungen hinzu.
Zum einen der oben genannten Punkt über Clojure Code enthält immer Verschlüsse kompiliert wird durch Funktionen höherer Ordnung und Funktionen von eval
Aufruf auf fn
/fn*
Formen zurück erstellt und in der Tat alles andere, als Clojure Funktion wirken kann. Somit benötigen Sie keine separate DSL-Funktionen zu beschreiben, nur Funktionen höherer Ordnung verwenden (und möglicherweise Makros):
(defn make-affine-function [a b]
(fn [x] (+ (* a x) b)))
((make-affine-function 31 47) 5)
; => 202
Dinge wäre interessanter, wenn Ihre Angaben Informationen über die Arten von Parametern enthalten waren, wie Dann könnten Sie daran interessiert sein, ein Makro zu schreiben, um mit diesen Typhinweisen Code zu generieren. Das einfachste Beispiel ich denken kann, wäre eine Variante der oben sein:
(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
Verwenden :int
, :long
, :float
oder :double
(oder die Nicht-Namespace-qualifizierten Symbole des entsprechenden Namen) als erstes Argument zu nutzen der ungepackten primitiven Arithmetik, die für Ihre Argumenttypen geeignet ist. Je nachdem, was Ihre Funktion macht, kann dies zu einer erheblichen Leistungssteigerung führen.
Andere Arten von Hinweisen sind normalerweise mit der #^Foo bar
-Syntax zur Verfügung gestellt (^Foo bar
macht das gleiche in 1.2); Wenn Sie sie zu Makro-generiertem Code hinzufügen möchten, untersuchen Sie die with-meta
Funktion (Sie müssen '{:tag Foo}
in die Metadaten der Symbole, die die formalen Argumente für Ihre Funktionen oder let
-introduced Einheimische, die Sie Typhinweise setzen möchten, zusammenführen)).
Oh, und für den Fall, würden Sie noch gerne wissen, wie Ihre ursprüngliche Idee zu implementieren ...
Sie können immer den Clojure Ausdruck erstellen, um Ihre Funktion zu definieren - (list 'fn ['x] (a-magic-function-to-generate-some-code some-args ...))
- und Call eval
über das Ergebnis.Das würde Ihnen erlauben, etwas wie das Folgende zu tun (es wäre einfacher zu verlangen, dass die Spezifikation die Parameterliste enthält, aber hier ist eine Version, die davon ausgeht, dass Argumente aus der Spezifikation herausgefischt werden sollen, werden paramFOO
genannt und sollen lexikografisch sortiert werden):
(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
Die überwiegende Mehrheit der Zeit, gibt es keinen guten Grund, Sachen auf diese Weise zu tun, und es sollte vermieden werden; Verwenden Sie stattdessen Funktionen und Makros höherer Ordnung. Wenn Sie jedoch etwas, sagen wir, evolutionäre Programmierung tun, dann ist es da und bietet die ultimative Flexibilität - und das Ergebnis ist immer noch eine kompilierte Funktion.
Das ist eine großartige Antwort: hat mir geholfen zu verstehen, was unter der Haube vor sich geht und löst das Problem perfekt. Vielen Dank Michal! – mikera
Gerne helfen. :-) –