【问题标题】:Let vs. Binding in Clojure让与 Clojure 中的绑定
【发布时间】:2010-12-04 03:22:12
【问题描述】:

我知道它们是不同的,因为一种用于设置 *compile-path* 而另一种则不能。但是,我需要帮助了解它们为何不同。

let 使用给定的绑定创建一个新作用域,但是binding...?

【问题讨论】:

    标签: binding clojure let


    【解决方案1】:

    let 为某个值创建一个词法范围的不可变别名。 binding 为一些 Var 创建一个动态范围的绑定。

    动态绑定意味着binding 表单中的代码以及该代码调用的任何代码(即使不在本地词法范围内)都会看到新的绑定。

    给定:

    user> (def ^:dynamic x 0)
    #'user/x
    

    binding 实际上为 Var 创建了一个动态绑定,但 let 仅使用本地别名隐藏 var:

    user> (binding [x 1] (var-get #'x))
    1
    user> (let [x 1] (var-get #'x))
    0
    

    binding 可以使用限定名称(因为它在Vars 上运行)而let 不能:

    user> (binding [user/x 1] (var-get #'x))
    1
    user> (let [user/x 1] (var-get #'x))
    ; Evaluation aborted.
    ;; Can't let qualified name: user/x
    

    let-引入的绑定是不可变的。 binding-introduced 绑定是线程本地可变的:

    user> (binding [x 1] (set! x 2) x)
    2
    user> (let [x 1] (set! x 2) x)
    ; Evaluation aborted.
    ;; Invalid assignment target
    

    词法与动态绑定:

    user> (defn foo [] (println x))
    #'user/foo
    user> (binding [x 1] (foo))
    1
    nil
    user> (let [x 1] (foo))
    0
    nil
    

    另请参阅Varslet

    【讨论】:

    • 这加上en.wikipedia.org/wiki/… 真的提高了我的理解。谢谢楼主!
    • x 必须与 ^:dynamic 提示绑定才能不抛出错误,我相信。
    【解决方案2】:

    let 与绑定的另一个语法差异:

    对于绑定,所有初始值都会在它们中的任何一个绑定到变量之前进行评估。这与 let 不同,您可以在后续定义中使用先前“别名”的值。

    user=>(let [x 1 y (+ x 1)] (println y))
    2
    nil
    
    user=>(def y 0)
    user=>(binding [x 1 y (+ x 1)] (println y))
    1
    nil
    

    【讨论】:

    • 您需要 ^:dynamic 将 x 定义为 0(也是动态的)以使您的第二个示例正常工作。
    【解决方案3】:

    binding 将值绑定到每个线程全局环境中的名称

    正如您所提到的,let 为所述绑定创建了一个新范围。

    【讨论】:

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