Будет ли компилятор clojure автоматически оценивать выражения литералов во время компиляции? - PullRequest
7 голосов
/ 19 августа 2010

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

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

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

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

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

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

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

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

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

Ответы [ 4 ]

6 голосов
/ 19 августа 2010

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

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

(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 значениям, полученным при оценке preprocess.

4 голосов
/ 19 августа 2010

Во-первых, существует довольно важная причина, по которой правые части 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))
    
    (defn take-your-time [t]
      (printf "Taking my time (%d)...\n" t)
      (Thread/sleep t))
    
    (def a (take-your-time 5000))
    
    (defmacro frozen-def [v e]
      (let [val (eval e)]
        `(def ~v ~val)))
    
    (frozen-def b (take-your-time 5000))
    
    (defn -main [& args]
      (println "Starting...")
      (println a)
      (println b))
    
  4. Пробег lein uberjar;конечно же, код занимает свое время дважды.

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

3 голосов
/ 19 августа 2010

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

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

0 голосов
/ 11 июня 2014

Для приведенных примеров выражения вычисляются во время компиляции / загрузки.def всегда оценивает свой второй аргумент.Из специальные формы

(def symbol init?)

Создает и интернирует или находит глобальный var с именем symbol и пространством имен значения текущего пространства имен *ns*. Если указано init, оно оценивается , и корневая привязка переменной var устанавливается на результирующее значение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...