Hamza Yerlikaya уже сделал самый важный момент, который является кодом Clojure всегда составлен. Я просто добавляю иллюстрации и некоторую информацию о некоторых низко висящих фруктах для ваших усилий по оптимизации.
Во-первых, выше пункт о коде Clojure всегда компилируется включает в себя закрытие возвращаемые функций и функций, созданных более высокого порядка по телефону eval
на fn
/fn*
формах и в самом деле что-либо другое, что может выступать в качестве функции Clojure. Таким образом, вам не нужен отдельный DSL для описания функций, просто использовать функции высшего порядка (и, возможно, макросы):
(defn make-affine-function [a b]
(fn [x] (+ (* a x) b)))
((make-affine-function 31 47) 5)
; => 202
вещей будет более интересно, если ваши данные были включать информацию о типах параметров, а то вам может быть интересно написать макрос для генерации кода с помощью этих типов подсказок. Простейший пример, который я могу думать будет вариант выше:
(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
Использование :int
, :long
, :float
или :double
(или не-пространства имен, символы соответствующих имен) в качестве первого аргумента, чтобы воспользоваться несвязанной примитивной арифметики, подходящей для ваших типов аргументов. В зависимости от того, что делает ваша функция, это может дать вам очень значительное повышение производительности.
Другие типы подсказок обычно снабжены синтаксисом #^Foo bar
(^Foo bar
делает то же самое в 1.2); если вы хотите добавить их к макросгенерированному коду, исследуйте функцию with-meta
(вам нужно объединить '{:tag Foo}
в метаданные символов, представляющих формальные аргументы, вашим функциям или let
- введенные местные жители, на которые вы хотите поместить подсказки типа).
О, и в случае, если вы все еще хотите знать, как реализовать свою оригинальную идею ...
Вы всегда можете построить выражение Clojure, чтобы определить свою функцию - (list 'fn ['x] (a-magic-function-to-generate-some-code some-args ...))
- и вызов eval
на результат.Это позволит вам сделать что-то вроде следующего (было бы проще потребовать, чтобы спецификация включала список параметров, но вот версия, предполагающая, что аргументы должны быть выведены из спецификации, все называются paramFOO
и должны быть лексикографически отсортированы):
(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
Подавляющее большинство времени, нет веских оснований для этого, и его следует избегать; вместо этого используйте функции более высокого порядка и макросы. Однако, если вы делаете что-то вроде, скажем, эволюционного программирования, то оно есть, обеспечивая максимальную гибкость - и результат все еще является скомпилированной функцией.
Это отличный ответ: помог мне понять, что происходит под капотом и прекрасно решает проблему. Большое спасибо Михалу! – mikera
Счастливые помочь. :-) –