使用“def”更新var并使用“alter-var-root”之间有什么区别? e.g。
(def x 3)
(def x (inc x))
VS
(def x 3)
(alter-var-root #'x inc)
答案 0 :(得分:26)
我发现alter-var-root很少出现在惯用的Clojure代码中;并不是说它有任何问题,它只适用于角落的情况。如果您发现自己使用它来构建循环,那么这是一个需要不同方法的标志。我主要在初始化例程中看到它用于设置访问凭证或记录器等。
alter-var-root
使用函数来机械地更改var的值,而def
只是将其设置为新值。在你的例子中,它们是等价的。
hello.exp> (def foo 4)
#'hello.exp/foo
hello.exp> (alter-var-root #'foo inc)
5
hello.exp> foo
5
alter-var-root
也不愿意创建新的var:
hello.exp> (alter-var-root #'foo1 inc)
CompilerException java.lang.RuntimeException: Unable to resolve var: foo1 in this context, compiling:(NO_SOURCE_PATH:1)
alter-var-root
也可以用于其他名称空间:
hello.exp> (in-ns 'user)
#<Namespace user>
user> (alter-var-root #'hello.exp/foo inc)
6
user> (def hello.exp/foo 4)
CompilerException java.lang.RuntimeException: Can't create defs outside of current ns, compiling:(NO_SOURCE_PATH:1)
user>
这最后一个用例是我在实践中唯一需要的用例。例如,强制clojure.logging
使用正确的slf4j记录器作为托盘项目的一个例子:
(defn force-slf4j
"The repl task brings in commons-logging, which messes up our logging
configuration. This is an attempt to restore sanity."
[]
(binding [*ns* (the-ns 'clojure.tools.logging.slf4j)]
(alter-var-root
#'clojure.tools.logging/*logger-factory*
(constantly (clojure.tools.logging.slf4j/load-factory)))))
使用alter-var-root
重置另一个命名空间中的var,无论其初始化内容如何。我想这有点像黑客......
答案 1 :(得分:17)
alter-var-root
提供了与函数应用程序相关的原子的附加值。 (alter-var-root #'foo inc)
的两个(可能是并发的)应用程序保证foo
将增加2.
(def x (inc x))
没有这样的保证。它可能会覆盖其他线程在读取x
的值和写入其更新值之间所做的任何更改。
另一方面,如果你使用alter-var-root
来表示其原子性,那么原子对你的用例来说可能比变量更好。
答案 2 :(得分:13)
使用def
:
(def w (vector)) ; create Var named w and bind it to an empty vector
(dotimes [x 9] ; repeat 9 times (keeping iteration number in x):
(future ; execute in other thread:
(def w ; replace root binding of w with
(conj w ; a new vector with all elements from previous (w)
x)))) ; with added an element indicating current iteration (x)
w ; get a value of Var's root binding (identified by symbol w)
; => [0 2 3 6 8 7 4 5] ; 1 is missing !!!
; second thread overlapped with another thread
; during read-conjoin-update and the other thread "won"
使用alter-var-root
:
(def w (vector)) ; create Var named w and bind it to an empty vector
(dotimes [x 9] ; repeat 9 times (keeping iteration number in x):
(future ; execute in other thread:
(alter-var-root #'w ; atomically alter root binding of w
(fn [old] ; by applying the result of a function,
(conj ; that returns a new vector
old ; containing all elements from previous (w)
x))))) ; with added an element indicating current iteration (x)
w ; get a value of Var's root binding (identified by symbol w)
; => [1 2 4 5 3 0 7 8 6]