我试图理解我在Clojure中注意到的一些行为。
可以使用多次重复的相同绑定名创建一个let绑定:
(let [a 1 a 2 a b] a)
; (= a 2)
(let [a 1 a 2 a 3] a)
; (= a 3)
我明白让绑定得到评估,这一切都很有意义。
我对文档的理解是"用let创建的本地不是变量。一旦创建,他们的价值观永远不会改变!"
上述语法是否实际更改了绑定的值?
这感觉它应该引起错误。
作为一种旁注:
有趣的是,您可以使用clojurescript输出上面的JS:
var a__36584 = 1, b__36585 = 2, a__36586 = b__36585;
var a__30671 = 1, a__30672 = 2, a__30673 = 3;
在这里,我们可以看到这些值实际上都是不同的变量,它们指向了封面下发生的事情,但是一些澄清会非常有用。
答案 0 :(得分:22)
(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)
之类的操作。
答案 1 :(得分:8)
let
与Common Lisp中的let*
行为相同,也就是说,它允许稍后使用的绑定。与重新结合相结合,这可能是有用的,例如当你需要以干净的方式删除某些数据层时:
(let [a some-vector, a (first a), a (:key a)] a)
当然这不是错误。正如您所注意到的,这些绑定在内部影响不同的变量。这基本上是clojure词汇变量的不变性。由于这个词汇变量,重新绑定具有干净的语义(最后一个绑定“胜利”),并且没有理由不允许它。
答案 2 :(得分:6)
其他答案正确地指出let语法有效地为隐藏旧绑定的新绑定创建了新的绑定。
值得注意的另一点是,当您知道某个值具有特定类型时,这对于优化Clojure代码非常有用,例如:
(let [d (double d)]
......)
在let块中,d将被转换然后用作原始double,这可以大大加快许多数学运算。