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

Why to use *let* in functions?

我不明白使用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) 

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

  1. 您想帮助程序员(包括您自己)了解他们到底是什么。
  2. 您想缓存它们以便可以在多个地方使用它们而无需再次计算表达式。如果评估表达式有副作用,并且您只希望它们发生一次,这一点尤其重要。
  3. 您正在创建一个 closure,它将使用 let 编辑的名称,但不会将其透露给外界。

在您的 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))

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

你的函数制作函数是

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

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

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

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

((make-adder 2) 4)
;6

(make-adder 2) 一个名字只会让人感到困惑。

显式定义,加上2的无名函数是

(fn [z] (+ 2 z))

将其应用到 4:

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

在大多数编程语言中,您可以声明和使用局部变量,这有助于您设计函数(或方法)主体。在 Clojure 中,let 允许您 绑定 局部符号到任何可以产生值、数据结构或其他局部函数定义的表达式。

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

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

最后但并非最不重要的一点是,let destructuring 在表达式 return 复合结构(如集合)时非常方便生成简洁的代码。