我花了一整天的时间研究Clojure(和Lisp)中的词法和动态范围。当我以为我终于明白了,我做了这个例子并且非常惊讶它没有回复我的预期:
(def non-dynamic-var "this is a non dynamic var")
(def ^:dynamic dynamic-var "this is a dynamic var")
(defn function-using-dynamic-var []
(println dynamic-var))
(defn function-using-non-dynamic-var []
(println non-dynamic-var))
(defn some-function []
(function-using-dynamic-var)
;; dynamically rebind dynamic-var
(binding [dynamic-var "this is some new content for the dynamic var"]
(function-using-dynamic-var))
(function-using-non-dynamic-var)
;; locally rebind non-dynamic-var
(let [non-dynamic-var "this is some new content that won't be used"]
(function-using-non-dynamic-var))
;; lexically rebind non-dynamic-var
(def non-dynamic-var "this is some new content that won't be used")
(function-using-non-dynamic-var))
我正在声明一个普通和动态var,然后是一个使用每个var的相应函数然后我尝试如果重新绑定/重新定义变量会发生什么。来自some-function
的呼叫的输出是:
this is a dynamic var
this is some new content for the dynamic var
this is a non dynamic var
this is a non dynamic var
this is some new content that won't be used
除最后一项外,输出符合预期。我无法理解为什么这会实际输出新值。由于我理解词法和动态范围,这应该仍然返回旧值,因为function-using-non-dynamic-var
在non-dynamic-var
中看到的函数在我看来应该仍然是第1行的定义,因为它是词汇范围。但显然我弄错了,任何人都能解释并理解它吗?
答案 0 :(得分:1)
好。在Clojure中,所有defs都是全球范围的。所有。在函数some-function
正文中包含defs。因此,在执行some-function
时,最后两个表单创建non-dynamic-var
的新全局定义,然后使用它调用函数,修改了var non-dynamic-var
的全局根绑定。
答案 1 :(得分:0)
我认为这是由于def
的实施方式。 def
创建一个Var - “Vars提供了一种机制来引用可以在每个线程的基础上动态反弹(到新的存储位置)的可变存储位置。” - http://clojure.org/vars。需要注意的是Vars
可变存储位置,并且可以反弹。
“如果def表达式在当前命名空间中找不到被定义符号的实习条目,则会创建一个,否则它将使用现有的Var。” - http://clojure.org/vars#Interning
通过运行以下内容,您可以更简单地看到此行为:
(def non-dynamic-var "this is a non dynamic var")
(println non-dynamic-var)
(def non-dynamic-var "this is our new value for the the non dynamic var")
(println non-dynamic-var)
这将打印:
this is a non dynamic var
this is our new value for the the non dynamic var