即使我使用过Clojure,我还没有详细查看范围规则。当我阅读文档时,我变得更加困惑。 我做了一个小测试来尝试确定范围,并对复杂性感到沮丧。有人可以解释Clojure使用的意图和各种规则吗?
(def x 1)
(defn dummy-fn2[]
(+ x 1))
(defn dummy-fn[]
(println "entering function: " x)
(let [x 100]
(println "after let: " x)
(let [x (dummy-fn2)]
(println "after let and dummy2: " x)
(binding [x 100]
(println "after binding: " x)
(let [x (dummy-fn2)]
(println "after binding and dummy2: " x))))))
1:2 foo=> (dummy-fn)
entering function: 1
after let: 100
after let and dummy2: 2
after binding: 2
after binding and dummy2: 101
nil
答案 0 :(得分:23)
let
使用本地x
隐藏顶级Var x
。 let
不会创建Var或影响顶级Var;它绑定一些符号,以便对该符号的本地引用将替换为let
- 绑定值。 let
具有词法范围,因此其绑定仅在let
表单本身可见(而不是在let
内调用的函数中)。
binding
更改了顶级Var x
的值,这就是它所做的一切。如果let
绑定到位,binding
在决定要更改哪个值时不会看到它(并且let
的绑定不是变量并且不可更改,因此'a好事还是它会给你一个错误)。并且binding
不会屏蔽let
。 binding
具有动态范围,因此它对顶级Vars的影响在binding
表单和从binding
表单内调用的任何函数中都可见。
访问普通旧x
的值将为您提供绑定堆栈顶部的任何内容,最嵌套的let
绑定值x
(或函数)名为x
的参数,或者如果您使用自己的宏或其他可能性,则替换为某个值x
,并且默认情况下仅使用顶级Var x
的当前值。没有其他约束力。
即使顶级Var x
被let
绑定x
掩盖,您也可以通过@#'x
访问顶级Var。试试这个版本,也许它会更有意义:
(def x 1)
(defn dummy-fn2[]
(println "x from dummy-fn2:" x)
(+ x 1))
(defn dummy-fn[]
(println "entering function:" x)
(println "var x:" @#'x)
(dummy-fn2)
(println "---")
(let [x 100]
(println "after let:" x)
(println "var x:" @#'x)
(dummy-fn2)
(println "---")
(let [x (dummy-fn2)]
(println "after let and dummy-fn2:" x)
(println "var x:" @#'x)
(dummy-fn2)
(println "---")
(binding [x 888]
(println "after binding:" x)
(println "var x:" @#'x)
(dummy-fn2)
(println "---")
(let [x (dummy-fn2)]
(println "after binding and dummy2:" x)
(println "var x:" @#'x)
(dummy-fn2)
(println "---"))))))
给出:
entering function: 1
var x: 1
x from dummy-fn2: 1
---
after let: 100
var x: 1
x from dummy-fn2: 1
---
x from dummy-fn2: 1
after let and dummy-fn2: 2
var x: 1
x from dummy-fn2: 1
---
after binding: 2
var x: 888
x from dummy-fn2: 888
---
x from dummy-fn2: 888
after binding and dummy2: 889
var x: 888
x from dummy-fn2: 888
---
答案 1 :(得分:14)
Clojure对符号使用词法范围 let
,对于vars使用动态范围 binding
查看clojure的vars文档。