使用"def"更新var和"alter-var-root"之间的区别

Ano*_*ous 25 clojure

使用"def"更新var并使用"alter-var-root"之间有什么区别?例如

(def x 3)
(def x (inc x))
Run Code Online (Sandbox Code Playgroud)

VS

(def x 3)
(alter-var-root #'x inc)
Run Code Online (Sandbox Code Playgroud)

Art*_*ldt 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
Run Code Online (Sandbox Code Playgroud)

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) 
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

最后一个用例是我在实践中唯一需要的用例.例如,强制clojure.logging使用正确的slf4j记录器作为Pallet项目的示例:

(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)))))
Run Code Online (Sandbox Code Playgroud)

这只是alter-var-root用于重置另一个命名空间中的var,无论其初始化内容如何.我想这有点像黑客......


Raf*_*ird 17

alter-var-root提供与函数应用程序相关的原子的附加值.两个(可能是并发)的应用(alter-var-root #'foo inc)保证foo将增加2.

(def x (inc x))没有这样的保证.它可能会覆盖其他线程在读取其值x和写入其更新值之间所做的任何更改.

另一方面,如果你正在使用alter-var-root它的原子性,那么原子可能比你的用例更好.

  • @amalloy `alter-var-root` 委托给 `clojure.lang.Var.alterRoot`,它是 `synchronized`。`bindRoot` 也是 `synchronized`,但当然它需要新的根值作为参数,因此在它尝试获取锁之前必须准备好。 (2认同)
  • 以下是1.5.1源代码的相关片段:[`clojure.core/alter-var-root`](https://github.com/clojure/clojure/blob/clojure-1.5.1/src/clj/ clojure/core.clj#L4941),[`clojure.lang.Var.alterRoot`](https://github.com/clojure/clojure/blob/clojure-1.5.1/src/jvm/clojure/lang/Var的.java#L335).编辑:docstring确实也提到了这个保证 - "原子改变......" - 根据Rafał的评论(因为我现在正在使用合适的浏览器;-)). (2认同)

sie*_*fca 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"
Run Code Online (Sandbox Code Playgroud)

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]
Run Code Online (Sandbox Code Playgroud)