bmi*_*are 41 constants clojure
我试图理解^:constclojure中的内容.这就是开发人员所说的.http://dev.clojure.org/display/doc/1.3
(def常数{:pi 3.14:e 2.71})
(def ^:const pi(:pi constants))(def ^:const e(:e constants))
查找的开销:e和:地图中的pi发生在编译时,因为(:pi常量)和(:e常量)在评估其父def形式时进行评估.
这是令人困惑的,因为元数据用于符号的var绑定pi,而var绑定到符号e,但下面的句子说它有助于加速地图查找,而不是var查找.
有人可以解释一下^:const正在做什么以及使用它背后的理由吗?请问这个比较使用一个巨大的let块或使用宏象(pi)和(e)?
Chr*_*ins 71
这对我来说似乎是一个不好的例子,因为关于地图查找的东西只是混淆了这个问题.
一个更现实的例子是:
(def pi 3.14)
(defn circumference [r] (* 2 pi r))
Run Code Online (Sandbox Code Playgroud)
在这种情况下,圆周体被编译成代码,在运行时解析pi(通过调用Var.getRawRoot),每次调用圆周.
(def ^:const pi 3.14)
(defn circumference [r] (* 2 pi r))
Run Code Online (Sandbox Code Playgroud)
在这种情况下,圆周被编译成完全相同的代码,就好像它是这样编写的:
(defn circumference [r] (* 2 3.14 r))
Run Code Online (Sandbox Code Playgroud)
也就是说,跳过了对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"
Run Code Online (Sandbox Code Playgroud)
Art*_*ldt 10
在示例文档中,他们试图表明,在大多数情况下,如果defvar是在不使用const的情况下在地图中查找某些内容的结果,则在类加载时将进行查找.因此,每次运行程序时都要支付一次费用(而不是在每次查找时,只是在加载类时).并且每次读取时都要花费在var中查找值的成本.
如果您改为使用const,则编译器将在编译时执行查找,然后发出一个简单的java final变量,并且在编译程序时只需支付一次查找总成本.
这是一个人为的例子,因为在类加载时的一次映射查找和运行时的一些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.
Run Code Online (Sandbox Code Playgroud)
因此,通过在定义变量时使用^:const元数据,变量可以有效地"内联"到它们所使用的每个位置.因此,对var的任何后续更改都不会影响已经内联"旧"值的任何代码.
^:const的使用也提供了文档功能.当读取(def ^:const pi 3.14159)告诉读者var pi不打算改变时,它只是值3.14159的一个方便(并且有希望描述性)的名称.
说到上面的内容,请注意我从未^:const在我的代码中使用过,因为它具有欺骗性,并且提供了"错误保证",即var永远不会改变.问题是^:const暗示一个人无法重新定义var,但正如我们所看到的那样,const-two并不能阻止var被改变.相反,^:const隐藏了var具有新值的事实,因为const-two在var更改之前(在运行时)已经复制/内联(在编译时)到每个使用位置.
一个更好的解决方案是在尝试更改^:constvar 时抛出异常.