为什么在函数中使用* let *?

时间:2015-05-09 06:43:19

标签: clojure

我不明白使用let有什么样的优点或缺点,因为可以避免在下面的代码中使用let?

(defn make-adder [x]
  (let [y x]
    (fn [z] (+ y z))))
(def add2 (make-adder 2))

;; or without let
(defn add2 [z] (+ 2 z))

(add2 4) 

3 个答案:

答案 0 :(得分:7)

命名中间结果的主要原因有三个:

  1. 您希望帮助程序员(包括您自己)了解它们的内容。
  2. 您希望对它们进行缓存,以便它们可以在多个位置使用,而无需再次评估表达式。如果评估您的表达式有副作用,并且您只希望它们发生一次,这一点尤为重要。
  3. 您正在创建一个closure,它将使用let - ed名称,但不会将其发送给外界。
  4. make-adder示例中,let并不真正需要,因为它只是为传入参数建立了别名。但如果你有更多的东西参与,那么这些优势开始变得相关。

    仅仅因为我手头上的问题,来自another recent answer of mine的一些代码:

    (defn trades-chan
      "Open the URL as a tab-separated values stream of trades. 
      Returns a core.async channel of the trades, represented as maps.
      Closes the HTTP stream on channel close!"
      [dump-url]
      (let[stream (-> dump-url
                     (client/get {:as :stream})
                     :body) ;;This is an example of 3. 
           lines  (-> stream
                     io/reader 
                     line-seq) 
           ;; This is an example of 2. I don't want to make multiple readers just because I use them in multiple expressions.
    
           ;;fields and transducer are examples of 1.
           fields (map keyword (str/split (first lines) #"\t")) 
           transducer (map (comp #(zipmap fields %) #(str/split % #"\t")))
    
           ;;output-chan is another example of 2  
           output-chan (async/chan 50 transducer)]
        (async/go-loop [my-lines (drop 1 lines)]
                       (if (async/>! output-chan (first my-lines))   
                         (recur (rest my-lines))         
                         (.close stream)))  ;;Here, the closure closes the HTTP stream, so it needs a name to refer to it by.             
        output-chan))
    

答案 1 :(得分:7)

在大多数编程语言中,您可以声明并使用局部变量来帮助您设计函数(或方法)主体。在Clojure中,let允许您将绑定本地符号绑定到任何可能导致值,数据结构或其他本地函数定义的表达式。

您的示例非常简单,不需要let构造,但您很快就会遇到对函数逻辑可读性有很大帮助的情况。

let也是一种分解代码的方法,它通过只计算一次表达式并在几个地方使用结果来提高可读性和性能。

最后但并非最不重要的是,当表达式返回像集合这样的复合结构时,let destructuring非常便于生成简洁的代码。

答案 2 :(得分:2)

问题不在于是否使用let,而是直接定义add2函数还是使用函数制作函数make-adder来制作它。

你的功能制作功能

(defn make-adder [x]
  (let [y x]
    (fn [z] (+ y z))))

正如Rust's regex engine所说,let在这里没有任何用处,最好省略,给出

(defn make-adder [x]
  (fn [z] (+ x z)))

...这更容易阅读。我们可以看到,给定一个数字,它返回一个函数,将该数字添加到它呈现的任何数字中:

((make-adder 2) 4)
;6

(make-adder 2)一个名字只是令人困惑。

明确定义它,添加2的无名函数是

(fn [z] (+ 2 z))

将其应用于4

((fn [z] (+ 2 z)) 4)
;6