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

use*_*828 1 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) 
Run Code Online (Sandbox Code Playgroud)

Mag*_*gos 7

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

  1. 您希望帮助程序员(包括您自己)了解它们的内容.
  2. 您希望缓存它们,以便它们可以在多个位置使用,而无需再次评估表达式.如果评估您的表达式有副作用,并且您只希望它们发生一次,这一点尤为重要.
  3. 您正在创建一个闭包,它将使用let-ed名称,但不会将其发送给外部世界.

在您的make-adder示例中,没有真正需要,let因为它只是为传入参数建立别名.但如果你有更多的东西参与,那么这些优势开始变得相关.

仅仅因为我有它,这里是我最近另一个答案的一些代码:

(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))
Run Code Online (Sandbox Code Playgroud)


T.G*_*lle 7

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

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

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

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