在let表单中定义匿名函数并重复调用外部函数是否存在性能损失?像这样:
(defn bar [x y]
(let [foo (fn [x] (* x x))]
(+ (foo x) (foo y))))
Run Code Online (Sandbox Code Playgroud)
与定义foo为单独的函数相比,如下所示:
(defn foo [x] (* x x))
(defn bar [x y] (+ (foo x) (foo y)))
Run Code Online (Sandbox Code Playgroud)
我理解foo这两种情况下的词汇范围是不同的,我只关心在多次调用时foo是否会反复定义函数bar.
我猜答案是否定的,即没有惩罚,但是clojure是怎么做到的?
谢谢!
let 本地方法:foo只编译一次(顶级表单时).这个编译的结果是一个实现clojure.lang.IFn接口的类; 实际的身体生活在invoke(Object)那个阶级的方法中.在运行时,每次控制到达的点bar在哪里foo当地介绍,这个类的一个新实例被分配; 这两个调用foo使用该类的实例.
这是在REPL中证明"单个编译"属性的简单方法:
(defn bar [x y]
(let [foo (fn [x] (* x x))]
foo))
(identical? (class (bar 1 2)) (class (bar 1 2)))
;= true
Run Code Online (Sandbox Code Playgroud)
NB.Clojure足够聪明,可以注意到它foo不是一个"实际闭包"(它关闭了参数bar,但它实际上并没有使用它们),所以运行时表示foo不带任何额外的字段,闭包会,但是foo然而,每次调用时都会分配新类的实例bar.
defn方法:有一个实例foo,但是调用它涉及通过Var的间接,它本身带有非零成本.除了最具性能敏感性的代码之外,这个成本通常不值得担心,但它就在那里,因此分解本地功能可能不一定是性能上的胜利.像往常一样,如果它值得优化,那么首先值得测量/基准测试.
let 过度 lambda还有丹尼尔提到的最终选项,其中包括let,而不是在里面defn.通过这种方法,有一个(类的)实例foo; 它存储在一个字段里面bar; 它用于所有foo内部调用bar.
| 归档时间: |
|
| 查看次数: |
99 次 |
| 最近记录: |