2013-05-08 43 views

Trả lời

23

tôi thấy thay đổi-var-gốc rất hiếm khi đi lên trong mã Clojure thành ngữ; không có gì sai với nó, nó chỉ dành cho trường hợp góc. Nếu bạn thấy mình sử dụng nó để xây dựng vòng lặp và như vậy đó là một dấu hiệu một cái gì đó cần một cách tiếp cận khác nhau. Tôi chủ yếu nhìn thấy nó trong thói quen khởi tạo để thiết lập thông tin truy cập hoặc logger và như vậy.

alter-var-root sử dụng hàm để thay đổi máy móc giá trị của biến số trong khi def chỉ đặt giá trị đó thành giá trị mới. Trong ví dụ của bạn, chúng tương đương nhau.

hello.exp> (def foo 4) 
#'hello.exp/foo 
hello.exp> (alter-var-root #'foo inc) 
5 
hello.exp> foo 
5 

alter-var-root cũng không muốn tạo ra một var mới:

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 thể làm việc trên không gian tên khác nữa:

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> 

trường hợp sử dụng cuối cùng này là người duy nhất tôi có cần thiết trong thực tế. Ví dụ buộc clojure.logging sử dụng slf4j logger đúng là một ví dụ từ dự án 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))))) 

nào chỉ được sử dụng để thiết lập lại alter-var-root một var trong không gian tên khác bất kể nội dung của nó trên khởi tạo. Tôi cho rằng đó là một chút hack ...

+1

Nhưng việc xác định var nhiều hơn một lần được coi là kiểu xấu vì 'def' luôn định nghĩa các vars cấp cao nhất. Nếu bạn muốn thay đổi ràng buộc của một số var, điều đúng đắn cần làm là sử dụng 'alter-var-root'. –

+1

Là một newbie, tôi cảm thấy câu trả lời này giải thích * khi bạn có thể sử dụng * 'alter-var-root' nhưng không ** tại sao ** bạn sẽ sử dụng nó. Một số trường hợp sử dụng phổ biến là gì? –

+0

Tôi sẽ bình luận về điều này –

15

alter-var-root cung cấp giá trị gia tăng là nguyên tử liên quan đến ứng dụng chức năng. Hai (có thể đồng thời) các ứng dụng của (alter-var-root #'foo inc) đảm bảo rằng foo sẽ tăng thêm 2.

Với không có bảo đảm như vậy. Nó có thể ghi đè lên bất kỳ thay đổi nào được thực hiện bởi các chủ đề khác giữa việc đọc giá trị x và ghi giá trị cập nhật của nó.

Mặt khác, nếu bạn đang sử dụng alter-var-root cho nguyên tử của nó thì có lẽ nguyên tử tốt hơn cho trường hợp sử dụng của bạn hơn là vars.

+0

Không có đảm bảo nguyên tử cho một trong hai cấu trúc. – amalloy

+0

Hoặc có lẽ tôi đang nghĩ đến việc tái định nghĩa ... có liên kết đến bảo lãnh? – amalloy

+1

@amalloy 'thay đổi-var-root' đại biểu để' clojure.lang.Var.alterRoot' được 'đồng bộ'. 'bindRoot' cũng được' đồng bộ hóa', nhưng tất nhiên nó lấy giá trị gốc mới làm đối số, do đó phải sẵn sàng trước khi nó cố gắng lấy khóa. –

11

Với 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" 

Với 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] 
Các vấn đề liên quan