6

我发现我很少let在 Clojure 中使用。出于某种原因,当我开始学习时,我不喜欢它,并且从那以后一直避免使用它。来的时候,感觉就像流动停止了一样let。我想知道,你认为我们可以完全没有它吗?

4

6 回答 6

12

让我们提供一些好处。首先,它允许在函数上下文中进行值绑定。其次,它具有可读性优势。因此,虽然从技术上讲,人们可以取消它(从某种意义上说,你仍然可以在没有它的情况下进行编程),但如果没有有价值的工具,这种语言就会变得贫乏。

let 的好处之一是它有助于形式化一种指定计算的通用(数学)方式,在这种方式中,您引入了方便的绑定,然后是一个简化的公式。很明显,绑定仅适用于该“范围”,并且与更数学的公式相关联是有用的,尤其是对于更多功能的程序员。

让块出现在 Haskell 等其他语言中并非巧合。

于 2012-11-28T23:49:56.187 回答
12

您可以替换任何出现的(let [a1 b1 a2 b2...] ...)by((fn [a1 a2 ...] ...) b1 b2 ...)所以是的,我们可以。虽然我经常使用 let ,但我宁愿不要没有它。

于 2012-11-28T23:50:02.737 回答
6

在防止宏中多次执行方面,Let 对我来说是必不可少的:

(defmacro print-and-run [s-exp]
   `(do (println "running " (quote ~s-exp) "produced " ~s-exp)
        s-exp))

会运行 s-exp 两次,这不是我们想要的:

(defmacro print-and-run [s-exp]
  `(let [result# s-exp]
    (do (println "running " (quote ~s-exp) "produced " result#)
        result#))

通过将表达式的结果绑定到名称并引用该结果两次来解决此问题。

因为宏返回的表达式将成为另一个表达式的一部分(宏是生成 s 表达式的函数),所以它们需要生成本地绑定以防止多次执行并避免符号捕获

于 2012-11-29T00:13:27.540 回答
5

我想我理解你的问题。如果有错请纠正我。有时“let”用于命令式编程风格。例如,

... (let [x (...)
          y (...x...)
          z (...x...y...)
          ....x...y...z...] ...

这种模式来自命令式语言:

... { x = ...;
      y = ...x...;
      ...x...y...;} ...

你避免这种风格,这就是为什么你也避免“让”,不是吗?

在某些问题中,命令式风格会减少代码量。此外,有时用 java 或 c 编写效率更高。同样在某些情况下,“let”只保存子表达式的值,而不管评估顺序如何。例如,

(... (let [a (...)
           b (...)...]
        (...a...b...a...b...) ;; still fp style
于 2012-11-29T03:22:45.177 回答
2

-bindings至少有两个重要的用例let

首先,let正确使用可以让你的代码更清晰、更短。如果你有一个多次使用的表达式,将它绑定在 alet中是非常好的。map这是使用的标准函数的一部分let

...
(let [s1 (seq c1) s2 (seq c2)]
  (when (and s1 s2)
    (cons (f (first s1) (first s2))
          (map f (rest s1) (rest s2)))))))
...

即使您只使用一次表达式,给它一个语义上有意义的名称仍然是有帮助的(对代码的未来读者)。

其次,正如 Arthur 所提到的,如果您想多次使用表达式的值,但只想对其进行一次评估,则不能简单地将整个表达式输入两次:您需要某种绑定。如果您有一个纯表达式,这将只是浪费:

user=> (* (+ 3 2) (+ 3 2))
25

但如果表达式有副作用,实际上会改变程序的含义:

user=> (* (+ 3 (do (println "hi") 2)) 
          (+ 3 (do (println "hi") 2)))
hi
hi
25

user=> (let [x (+ 3 (do (println "hi") 2))] 
         (* x x))                      
hi
25
于 2012-12-14T16:07:03.017 回答
0

最近偶然发现了这个,所以运行了一些时间:

(testing "Repeat vs Let vs Fn"
  (let [start (System/currentTimeMillis)]
    (dotimes [x 1000000]
      (* (+ 3 2) (+ 3 2)))
    (prn (- (System/currentTimeMillis) start)))

  (let [start (System/currentTimeMillis)
        n (+ 3 2)]
    (dotimes [x 1000000]
      (* n n))
    (prn (- (System/currentTimeMillis) start)))

  (let [start (System/currentTimeMillis)]
    (dotimes [x 1000000]
      ((fn [x] (* x x)) (+ 3 2)))

    (prn (- (System/currentTimeMillis) start)))))

Output
Testing Repeat vs Let vs Fn
116
18
60

“让”胜过“纯”功能。

于 2014-08-26T23:31:18.170 回答