Будет ли компилятор clojure автоматически оценивать выражения литералов при компиляции время?

Это может быть глупый вопрос, но:

Предположим, что выражение зависит только от литералов или от других выражений, которые также зависят только от литералов; будет ли компилятор оценивать это во время компиляции?

Предположим, у меня есть,

(def a (some-time-consuming-function some-literal))

(def b (some-other-time-consuming-function a))

Будет ли b и a полностью оцениваться во время компиляции, чтобы пользователь не был затронут?

РЕДАКТИРОВАТЬ: Большое спасибо всем из ответов были очень полезны.

ИЗМЕНИТЬ 6.6.2011: Оказывается, что если вы попытаетесь использовать эту технику для предварительного вычисления очень большой структуры данных, легко будет создать файлы классов, которые слишком велики для загрузки. В этих случаях вы хотите создать файл, который будет читаться, а не файл класса, который будет загружен. Макро трюки, описанные в этих ответах, должны применяться только в том случае, если возвращаемое значение не является чрезмерно большой структурой.

Выдается сообщение об ошибке: «java.lang.ClassFormatError: Неверный индекс этого класса» См. эту ветку для обсуждения связанной ситуации.

7
задан Rob Lachlan 7 June 2011 в 05:09
поделиться

3 ответа

Совсем не глупо, я должен был подумать и проверить это.

Это будет работать, только если вы используете макросы вместо функций, поскольку тело макроса оценивается во время компиляции / расширения макроса. Например: [

(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 для значений, возвращаемых при оценке препроцесса .

6
ответ дан 6 December 2019 в 19:30
поделиться

В вашем примере трудоемкие функции вызываются только один раз, когда ваш код загружен.

Компилятор Clojure не пытается оптимизировать константные выражения, но в некоторых случаях компилятор Java JIT может это сделать.

3
ответ дан 6 December 2019 в 19:30
поделиться

Во-первых, есть довольно важная причина, по которой правые части def должны оцениваться во время загрузки: они могут как-то зависеть от среды и в общем случае невозможно сказать, есть они или нет. Возьмите, например,

(def *available-processors* (.availableProcessors (Runtime/getRuntime)))

Во-вторых, вот один из подходов к тестированию того, что происходит на самом деле:

  1. Создайте тестовый проект с Leiningen - скажем, lein new testdefs .

  2. Поместите : main testdefs.core в project.clj .

  3. Поместите в 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))
    
  4. Выполнить lein uberjar ; конечно же, код отнимает время дважды.

  5. Запустите java -jar testdefs-1.0.0-SNAPSHOT-standalone.jar ; вы заметите, что код занимает свое время только один раз.

4
ответ дан 6 December 2019 в 19:30
поделиться