5

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

这是一个简单的虚构示例 - 将变量数值添加到原子 - 显示两种方法:

(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)

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

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

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

谢谢!

编辑以回应 A. Webb 在下面的回答(这太长了,无法发表评论):

My use of the word "efficiency" in the question was misleading. Better words might have been "elegance" or "simplicity."

One of the things that I like about Clojure is that while you can write code to execute any particular algorithm faster in other languages, if you write idiomatic Clojure code it's going to be decently fast, and it's going to be simple, elegant, and maintainable. As the problems you're trying to solve get more complex, the simplicity, elegance and maintainability get more and more important. IMO, Clojure is the most "efficient" tool in this sense for solving a whole range of complex problems.

My question was really -- given that there are two ways that I can solve this problem, what's the more idiomatic and Clojure-esque way of doing it? For me when I ask that question, how 'fast' the two approaches are is one consideration. It's not the most important one, but I still think it's a legitimate consideration if this is a common pattern and the different approaches are a wash from other perspectives. I take A. Webb's answer below to be, "Whoa! Pull back from the weeds! The compiler will handle either approach just fine, and the relative efficiency of each approach is anyway unknowable without getting deeper into the weeds of target platforms and the like. So take your hint from the name of the language and when it makes sense to do so, use closures."

closing edit on April 10, 2014

I'm going to mark A. Webb's answer as accepted, although I'm really accepting A. Webb's answer and omiel's answer -- unfortunately I can't accept them both, and adding my own answer that rolls them up seems just a bit gratuitous.

One of the many things that I love about Clojure is the community of people who work together on it. Learning a computer language doesn't just mean learning code syntax -- more fundamentally it means learning patterns of thinking about and understanding problems. Clojure, and Lisp behind it, has an incredibly powerful set of such patterns. For example, homoiconicity ("code as data") means that you can dynamically generate code at compile time using macros, or destructuring allows you to concisely and readably unpack complex data structures. None of the patterns are unique to Clojure, but Clojure brings them all together in ways that make solving problems a joy. And the only way to learn those patterns is from people who know and use them already. When I first picked Clojure more than a year ago, one of the reasons that I picked it over Scala and other contenders was the reputation of the Clojure community for being helpful and constructive. And I haven't been disappointed -- this exchange around my question, like so many others on StackOverflow and elsewhere, shows how willing the community is to help a newcomer like me -- thank you!

4

2 回答 2

2

After you figure out the implementation details of the current compiler version for the current version of your current target host, then you'll have to start worrying about the optimizer and the JIT and then the target computer's processors.

You are too deep in the weeds, turn back to the main path.

Closing over free variables when applicable is the natural thing to do and an extremely important idiom. You may assume a language named Clojure has good support for closures.

于 2014-03-08T12:24:23.930 回答
1

I prefer the first approach as being simpler (as long as the closure is simple) and somewhat easier to read. I often struggle reading code where you have an anonymous function immediately called with parameters ; I have to resolve to count parentheses to be sure of what's happening, and I feel it's not a good thing.

I think the only way it could be the wrong thing to do is if the closures closes over a value that shouldn't be captured, like the head of a long lazy sequence.

于 2014-03-08T19:17:38.300 回答