提问者:小点点

Clojure闭包效率?


很多时候,我交换!使用匿名函数的原子值,该函数在计算新值时使用一个或多个外部值。有两种方法可以做到这一点,一种是闭包,另一种不是,我的问题是哪种方法更好/更有效?

这是一个简单的虚构示例——向原子添加一个可变数值——显示了两种方法:

(def my-atom (atom 0))

(defn add-val-with-closure [n]
  (swap! my-atom 
         (fn [curr-val] 
           ;; we pull 'n' from outside the scope of the function
           ;; asking the compiler to do some magic to make this work
           (+ curr-val n)) ))

(defn add-val-no-closure [n]
  (swap! my-atom 
         (fn [curr-val val-to-add] 
           ;; we bring 'n' into the scope of the function as the second function parameter
           ;; so no closure is needed
           (+ curr-val val-to-add))
         n))

这是一个虚构的例子,当然,您实际上不会编写这段代码来解决这个特定问题,因为:

(swap! my-atom + n)

做同样的事情而不需要任何额外的功能。

但是在更复杂的情况下,你确实需要一个函数,然后问题就出现了。对我来说,从编码的角度来看,这两种解决问题的方法的复杂度大致相同。如果是这种情况,我应该更喜欢哪一种?我的工作假设是非闭包方法更好(因为编译器实现起来更简单)。

还有第三种方法可以解决这个问题,那就是不使用匿名函数。如果你使用一个单独的命名函数,那么你就不能使用闭包,问题就不会出现。但是内联匿名函数通常会使代码更具可读性,我想把这种模式留在我的工具包中。

谢谢!

编辑以回应下面韦伯的回答(这太长了,无法发表评论):

我在问题中使用“效率”这个词是误导性的。更好的词可能是“优雅”或“简单”

我喜欢Clojure的一点是,虽然你可以用其他语言编写代码来更快地执行任何特定的算法,但如果你编写惯用的Clojure代码,它会相当快,并且简单、优雅和可维护。随着你试图解决的问题变得越来越复杂,简单、优雅和可运维性变得越来越重要。IMO,Clojure是解决一系列复杂问题的最“有效”的工具。

我的问题实际上是——鉴于有两种方法可以解决这个问题,哪种方法更惯用和Clojure风格?对我来说,当我问这个问题时,这两种方法有多“快”是一个考虑因素。这不是最重要的一个,但我仍然认为如果这是一种常见的模式,不同的方法从其他角度来看是一种洗涤,这是一个合理的考虑因素。我把A. Webb下面的回答理解为,“哇!从杂草中撤退!编译器可以很好地处理任何一种方法,并且在不深入目标平台等杂草的情况下,每种方法的相对效率无论如何都是不可知的。因此,从语言的名称中获取提示,当这样做有意义时,使用闭包。”

2014年4月10日结束编辑

我要把A. Webb的答案标记为已接受,尽管我真的接受A.Webb的答案和omiel的答案——不幸的是,我不能同时接受它们,并且添加我自己的答案来汇总它们似乎有点无端。


共2个答案

匿名用户

在确定当前目标主机当前版本的当前编译器版本的实现细节后,您将不得不开始担心优化器和JIT,然后是目标计算机的处理器。

你在杂草中陷得太深了,转回到正道。

在适用时关闭自由变量是很自然的事情,也是一个极其重要的习惯用法。您可能会假设一种名为Clojure的语言对闭包有很好的支持。

匿名用户

我更喜欢第一种方法,因为它更简单(只要闭包简单),而且更容易阅读。我经常难以阅读代码,其中有一个匿名函数立即用参数调用;我必须下定决心计算括号以确定发生了什么,我觉得这不是一件好事。

我认为唯一可能是错误的方法是,如果闭包关闭了一个不应该被捕获的值,比如一个长惰性序列的头部。