Это может быть глупый вопрос, но:
Предположим, что выражение зависит только от литералов или от других выражений, которые также зависят только от литералов; будет ли компилятор оценивать это во время компиляции?
Предположим, у меня есть,
(def a (some-time-consuming-function some-literal))
(def b (some-other-time-consuming-function a))
Будет ли b и a полностью оцениваться во время компиляции, чтобы пользователь не был затронут?
РЕДАКТИРОВАТЬ: Большое спасибо всем из ответов были очень полезны.
ИЗМЕНИТЬ 6.6.2011: Оказывается, что если вы попытаетесь использовать эту технику для предварительного вычисления очень большой структуры данных, легко будет создать файлы классов, которые слишком велики для загрузки. В этих случаях вы хотите создать файл, который будет читаться, а не файл класса, который будет загружен. Макро трюки, описанные в этих ответах, должны применяться только в том случае, если возвращаемое значение не является чрезмерно большой структурой.
Выдается сообщение об ошибке: «java.lang.ClassFormatError: Неверный индекс этого класса» См. эту ветку для обсуждения связанной ситуации.
Совсем не глупо, я должен был подумать и проверить это.
Это будет работать, только если вы используете макросы вместо функций, поскольку тело макроса оценивается во время компиляции / расширения макроса. Например: [
(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))
] Затем a
и b
являются def
'd для значений, возвращаемых при оценке препроцесса
.
В вашем примере трудоемкие функции вызываются только один раз, когда ваш код загружен.
Компилятор Clojure не пытается оптимизировать константные выражения, но в некоторых случаях компилятор Java JIT может это сделать.
Во-первых, есть довольно важная причина, по которой правые части def
должны оцениваться во время загрузки: они могут как-то зависеть от среды и в общем случае невозможно сказать, есть они или нет. Возьмите, например,
(def *available-processors* (.availableProcessors (Runtime/getRuntime)))
Во-вторых, вот один из подходов к тестированию того, что происходит на самом деле:
Создайте тестовый проект с Leiningen - скажем, lein new testdefs
.
Поместите : main testdefs.core
в project.clj
.
Поместите в src / testdefs / core.clj
следующее:
(ns testdefs.core
(: gen-class))
(определенно не торопитесь [t]
(printf "Не торопясь (% d) ... \ n" t)
(Нить / спать т))
(по умолчанию (не торопитесь 5000))
(defmacro frozen-def [v e]
(пусть [val (eval e)]
`(def ~ v ~ val)))
(замороженный def b (не торопитесь 5000))
(defn -main [& аргументы]
(println "Запускается ...")
(печать)
(println b))
Выполнить lein uberjar
; конечно же, код отнимает время дважды.
Запустите java -jar testdefs-1.0.0-SNAPSHOT-standalone.jar
; вы заметите, что код занимает свое время только один раз.