【问题标题】:Clojure let allows multiple bindings with the same nameClojure let 允许多个同名绑定
【发布时间】:2012-04-11 16:03:16
【问题描述】:

我试图了解我在 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;

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

【问题讨论】:

    标签: clojure clojurescript


    【解决方案1】:

    (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) 的事情。

    【讨论】:

      【解决方案2】:

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

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

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

      【讨论】:

        【解决方案3】:

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

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

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

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

        【讨论】:

          猜你喜欢
          • 2014-03-21
          • 2020-03-29
          • 2014-06-19
          • 2013-04-19
          • 1970-01-01
          • 2013-05-31
          • 2015-11-20
          • 2012-08-20
          • 2014-01-23
          相关资源
          最近更新 更多