Clojure中的Let vs. Binding


69

我知道它们是不同的,因为一种适用于设置,*compile-path*而另一种不适用。但是,我需要帮助他们为什么与众不同。

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

Answers:


107

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

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

鉴于:

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

binding实际上为创建了一个动态绑定,Varlet仅使用本地别名遮盖了var:

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

binding可以使用限定名称(因为它在上运行Var),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-引入的绑定是线程局部可变的:

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

另请参见Vars


5
加上en.wikipedia.org/wiki/…确实加深了我的理解。谢谢你,先生!
卡尔,

1
我相信,x必须与^:dynamic提示绑定,以免引发错误。
WeGi 2014年

12

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

对于绑定,在将所有初始值绑定到var之前先评估所有初始值。这与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(也是动态地),以使第二个示例正常工作。
约翰

9

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

如您所述,let为所述绑定创建一个新范围。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.