Clojure如何工作?

我想了解什么^:const在clojure中做的。 这是开发者文档所说的。 http://dev.clojure.org/display/doc/1.3

(def常量{:pi 3.14:e 2.71})

(def ^:const pi(:pi常量))(def ^:const e(:e常量))

查找的开销:在映射中的e和:pi在编译时发生,因为(:pi常量)和(:e常量)在评估它们的父级forms时被评估。

这是令人困惑的,因为元数据是用于绑定到符号pi的var,而绑定到符号e的var,但下面的语句表示它有助于加速地图查找,而不是var查找。

有人可以解释一下^:const在做什么以及使用它的原理吗? 这与使用巨let块或像(pi)(e)这样的macros比较起来如何?

对我来说,这看起来是个不好的例子,因为关于map-lookup的东西只是混淆了这个问题。

更现实的例子是:

 (def pi 3.14) (defn circumference [r] (* 2 pi r)) 

在这种情况下,周长主体被编译成代码,在运行时(通过调用Var.getRawRoot)取消引用pi,每次调用周长。

 (def ^:const pi 3.14) (defn circumference [r] (* 2 pi r)) 

在这种情况下,圆周被编译成完全相同的代码,就像它是这样写的:

 (defn circumference [r] (* 2 3.14 r)) 

也就是说,调用Var.getRawRoot会被跳过,这会节省一点时间。 这是一个快速的测量,其中circ是上面的第一个版本,而circ2是第二个:

 user> (time (dotimes [_ 1e5] (circ 1))) "Elapsed time: 16.864154 msecs" user> (time (dotimes [_ 1e5] (circ2 1))) "Elapsed time: 6.854782 msecs" 

在示例文档中,他们试图说明,在大多数情况下,如果将var定义为在不使用const的情况下在地图中查找某个东西的结果,那么查找将在类加载时发生。 所以每次运行程序时都要付一次费用 (而不是每次查找,只是在课程加载的时候)。 并且支付每次读取var时查找var中的值的代价。

如果你改为const,那么编译器会在编译时执行查找操作,然后发出一个简单的java finalvariables,在编译程序时你只需要支付一次查找开销

这是一个人为的例子,因为在类加载时的一个映射查找和在运行时的一些var查找基本上是没有的,虽然它说明了一些工作发生在编译时,一些在加载时间,其余的好…其余的时间

除了上述效率方面之外,还有一个安全方面也是有用的。 考虑下面的代码:

 (def two 2) (defn times2 [x] (* two x)) (assert (= 4 (times2 2))) ; Expected result (def two 3) ; Ooops! The value of the "constant" changed (assert (= 6 (times2 2))) ; Used the new (incorrect) value (def ^:const const-two 2) (defn times2 [x] (* const-two x)) (assert (= 4 (times2 2))) ; Still works (def const-two 3) ; No effect! (assert (= 3 const-two )) ; It did change... (assert (= 4 (times2 2))) ; ...but the function did not. 

因此,在定义variables时,通过使用^:const元数据,variables被有效地“内联”到每个使用的地方。 因此,对var的任何后续更改都不会影响已经内联“old”值的任何代码。

使用^:const也提供了一个文档function。 当读取(def ^:const pi 3.14159)是告诉读者var pi永远不会改变,它只是一个方便(&希望描述性)的值3.14159的名字。

说了上面的话,请注意,我从来没有在我的代码中使用^:const ,因为它是欺骗性的,并且提供了一个var不会改变的“错误保证”。 问题是^:const意味着不能重新定义一个var,但正如我们在const-two看到的那样,它不能防止var被改变。 相反, ^:const隐藏了var有一个新值的事实,因为在var被改变之前(在运行时) const-two被复制/内联(在编译时)到每个使用位置。

更好的解决scheme是尝试更改^:const var时抛出exception。