12

我试图了解我在 Clojure 中注意到的一些行为。

可以使用重复多次的相同绑定名称创建一个 let 绑定:

(let [a 1 a 2 a b] a)
; (= a 2)

(let [a 1 a 2 a 3] a)
; (= a 3)

我知道 let 绑定被评估,这一切都是有意义的。

我对文档的理解是“用 let 创建的本地变量不是变量。一旦创建,它们的值就永远不会改变!”

上面的语法是否真的改变了绑定的值?

这感觉应该引发错误。

作为一种旁注:

有趣的是,您可以使用 clojurescript 将上述内容输出为 JS:

var a__36584 = 1, b__36585 = 2, a__36586 = b__36585;
var a__30671 = 1, a__30672 = 2, a__30673 = 3;

在这里,我们可以看到这些值实际上都是不同的变量,这表明了幕后发生的事情,但一些澄清会非常有帮助。

4

3 回答 3

24

(let [a 1, a 2] a)在功能上等同于(let [a 1] (let [a 2] a)),这可能更容易理解。在后一种情况下,很容易意识到您不是在“修改” 的值a,而是引入了一个新的、不相关的变量a,并以不同的值命名。你可以看到这样的效果(let [a 1] (let [a 2] (println a)) a)- 它打印 2,然后返回 1,因为外部a永远不会改变,只是暂时隐藏。(let [a 1, a 2] a)只是引入了一个名为a立即超出范围的值。当然,外部a是可用的,直到内部a有一个值,所以你可以做类似的事情(let [a 1, a (inc a)] a)

于 2012-03-28T05:43:10.930 回答
10

letin clojure 的行为类似于let*Common Lisp,也就是说,它允许以后的绑定更早地使用。结合重新绑定,这可能很有用,例如,当您需要以干净的方式删除某些数据层时:

(let [a some-vector, a (first a), a (:key a)] a)

当然,这不是错误。正如您所注意到的,这些绑定在内部会影响不同的变量。这本质上是 clojure 词法变量的不变性。因为这个词法变量重新绑定具有清晰的语义(最后一个绑定“获胜”),并且没有理由禁止它。

于 2012-03-28T05:42:24.613 回答
7

其他答案正确地指出 let 语法有效地为隐藏旧绑定的 a 创建新绑定。

需要注意的一个有趣的附加点是,当您知道一个值将具有特定类型时,这对于优化 Clojure 代码非常有用,例如:

(let [d (double d)]
  ......)

在 let 块中, d 将被强制转换,然后用作原始双精度数,这可以大大加快许多数学运算。

于 2012-03-28T10:57:27.473 回答