为什么Clojure中的time需要实现为宏而不能用普通函数?
为什么Clojure的time要实现为宏
首先我们先看time的官方实现:
(defmacro time "Evaluates expr and prints the time it took. Returns the value of expr." {:added "1.0"} [expr] `(let [start# (. System (nanoTime)) ret# ~expr] (prn (str "Elapsed time: " (/ (double (- (. System (nanoTime)) start#)) 1000000.0) " msecs")) ret#))
核心原因和普通函数无法实现的点,都和Clojure的求值规则、宏的编译期展开特性有关:
- 求值时机可控:Clojure的普通函数遵循应用序求值,调用时会先把所有入参求值完成再传入函数体。如果
time是普通函数,你调用(time (heavy-calc 10000))的时候,heavy-calc会先执行完得到结果再传给time,完全没法统计目标代码的执行耗时。而宏是在编译期把传入的表达式直接插入到展开后的代码里,我们可以在目标表达式执行前先打点,执行完再算耗时,刚好匹配计时的需求。 - 无额外开销、计时更准确:宏是编译期展开,最终执行的代码和你手写计时逻辑的效果完全一致,没有额外的函数调用开销,统计出来的耗时更贴近实际执行时间。
- 完全保留目标表达式的行为:宏展开后目标表达式是在调用方的词法上下文里执行的,表达式里用到的局部变量、外部绑定都能正常解析,而且可以原封不动返回目标表达式的执行结果,调用
time完全不会影响原有代码的逻辑和返回值,用普通函数很难做到完全无侵入的效果。
内容的提问来源于stack exchange,提问作者J. Mini
相关产品推荐
相关产品推荐

