clojure编译器会在编译时自动计算文字表达式吗?

时间:2010-08-18 22:02:53

标签: compiler-construction 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:此类索引无效” 有关相关情况的讨论,请参阅this thread

4 个答案:

答案 0 :(得分:6)

根本不傻,我不得不考虑并测试它。

只有在使用宏而不是函数时才会起作用,因为宏的主体是在compile / macroexpansion-time时计算的。 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))

然后ab对评估def返回的值preprocess了解。

答案 1 :(得分:4)

首先,有一个非常重要的原因,为什么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;你会注意到代码只花了一次时间。

答案 2 :(得分:3)

在您的示例中,加载代码时,只需调用一次耗时的函数。

Clojure编译器不会尝试优化常量表达式,但Java JIT编译器在某些情况下可能会这样做。

答案 3 :(得分:0)

对于给出的示例,表达式在编译/加载时进行评估。 def 总是评估其第二个参数。来自special forms

(def symbol init?)

创建并实习或定位名为symbol的全局var以及当前名称空间*ns*的值的命名空间。 如果提供了init,则会对其进行评估,并将var的根绑定设置为结果值。