Jaka jest różnica między używaniem "def" do aktualizacji var i używanie "alter-var-root"? np.Różnica między używaniem "def" do aktualizacji var i "alter-var-root"
(def x 3)
(def x (inc x))
vs
(def x 3)
(alter-var-root #'x inc)
Jaka jest różnica między używaniem "def" do aktualizacji var i używanie "alter-var-root"? np.Różnica między używaniem "def" do aktualizacji var i "alter-var-root"
(def x 3)
(def x (inc x))
vs
(def x 3)
(alter-var-root #'x inc)
znajdę alter-var root bardzo rzadko pojawia się w idiomatycznym kodu Clojure; nie chodzi o to, że coś jest nie w porządku, jest to po prostu przeznaczone na przypadki narożne. Jeśli używasz go do budowania pętli i taki jest znak, że coś wymaga innego podejścia. Zwykle widzę to w procedurach inicjalizacji dla ustawiania poświadczeń dostępu lub rejestratorów i tym podobnych.
alter-var-root
wykorzystuje funkcję do mechanicznej zmiany wartości zmiennej, podczas gdy def
po prostu ustawia ją na nową wartość. W twoim przykładzie są one równoważne.
hello.exp> (def foo 4)
#'hello.exp/foo
hello.exp> (alter-var-root #'foo inc)
5
hello.exp> foo
5
alter-var-root
jest również chce utworzyć nową 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
może pracować na innych nazw, a także:
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>
Ten ostatni przypadek użycia jest tylko jeden mam kiedykolwiek potrzebne w praktyce. Na przykład zmuszając clojure.logging
używać poprawnej slf4j rejestratora jako przykład z projektu palecie:
(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)))))
który jest po prostu za pomocą alter-var-root
zresetować var w innej przestrzeni nazw, niezależnie od jej treści na inicjalizacji. Przypuszczam, że to trochę hack ...
Ale definiowanie var więcej niż jeden raz jest uważane za zły styl, ponieważ 'def' zawsze definiuje vars najwyższego poziomu. Jeśli chcesz zmienić powiązanie jakiegoś var, dobrym wyjściem jest użycie 'alter-var-root'. –
Jako początkujący, czuję, że ta odpowiedź wyjaśnia * kiedy możesz użyć * 'alter-var-root', ale nie ** dlaczego ** używałbyś tego. Jakie są typowe przypadki użycia? –
Będę komentować ten –
alter-var-root
zapewnia wartość dodaną bycia atomowym w odniesieniu do aplikacji funkcji. Dwa (prawdopodobnie współbieżne) aplikacje z zakresu (alter-var-root #'foo inc)
gwarantują, że foo
wzrośnie o 2.
Z (def x (inc x))
nie ma takiej gwarancji. Może to zastąpić wszelkie zmiany dokonane przez inne wątki między odczytaniem wartości x
i zapisaniem jej zaktualizowanej wartości.
Z drugiej strony, jeśli używasz alter-var-root
dla swojej atomowości, to być może atomy są lepsze dla twojego przypadku użycia niż vary.
Nie ma gwarancji atomowości dla obu konstrukcji. – amalloy
A może mam na myśli z-redefs ... mam link do gwarancji? – amalloy
@amalloy 'alter-var-root' deleguje do' clojure.lang.Var.alterRoot', który jest 'zsynchronizowany'. 'bindRoot' jest również' zsynchronizowany', ale oczywiście przyjmuje nową wartość root jako argument, który musi być więc gotowy przed próbą uzyskania blokady. –
Z 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"
Z 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]
Obie odpowiedzi były bardzo pouczające, dziękuję! Chciałabym móc je przyjąć. – Anonymous