2012-07-30 25 views
24

Tôi cố gắng để hiểu các biến động và chức năng ràng buộc vì vậy tôi cố gắng này (clojure 1.3):clojure và ^: năng động

user=> (defn f [] 
      (def ^:dynamic x 5) 
      (defn g [] (println x)) 
      (defn h [] (binding [x 3] (g))) 
      (h)) 
#'user/f 
user=> (f)  
5 
nil 

Bối rối, tôi đã cố gắng mã này hơi đơn giản:

user=> (def ^:dynamic y 5) 
#'user/y 
user=> (defn g [] (println y)) 
#'user/g 
user=> (defn h [] (binding [y 3] (g))) 
#'user/h 
user=> (h) 
3 
nil 

gì khác nhau giữa hai đoạn mã là gì? Tại sao ví dụ thứ hai lại làm việc nhưng ví dụ đầu tiên lại không?

Gợi ý: Tôi chỉ nhận ra rằng các công việc sau (vẫn không hoàn toàn hiểu tại sao):

user=> (def ^:dynamic y 5) 
#'user/y 
user=> (defn f [] (defn g [] (println y)) (defn h [] (binding [y 3] (g))) (h)) 
#'user/f 
user=> (f) 
3 
nil 
user=> 

Trả lời

26

tôi nhận được 3 kết quả là (như bạn mong chờ) khi tôi chạy ví dụ đầu tiên của bạn trong Clojure 1.4 .... bạn đã thử với REPL mới chưa?

^:dynamic là một hướng dẫn cho trình biên dịch Clojure rằng biểu tượng (như được định nghĩa với def) được dự định sẽ được khôi phục động (với binding).

Ví dụ:

(def foo 1) 
(binding [foo 2] foo) 
=> IllegalStateException Can't dynamically bind non-dynamic var: ... 

(def ^:dynamic bar 10) 
(binding [bar 20] bar) ;; dynamically bind bar within the scope of the binding 
=> 20 
bar      ;; check underlying value of bar (outside the binding) 
=> 10 

Lưu ý rằng binding có phạm vi năng động trong thread gọi - bất kỳ chức năng gọi trong các ràng buộc sẽ thấy giá trị sửa đổi của bar (20), nhưng bất kỳ chủ đề khác vẫn sẽ thấy những thay đổi giá trị gốc của 10

Cuối cùng một vài điểm phong cách mà bạn có thể thấy hữu ích:

  • đó là nói chung c đưa ra ý tưởng không hợp lệ để đặt defdefn trong các hàm khi chúng ảnh hưởng đến không gian tên kèm theo. Trong các hàm, bạn nên sử dụng (let [foo bar] ...) để thay thế.
  • Khi bạn thấy mình muốn sử dụng binding, bạn thường nên cân nhắc xem bạn có thể đạt được kết quả tương tự bằng cách sử dụng hàm bậc cao hơn thay thế hay không. binding là hữu ích trong một số ngữ cảnh nhưng nó không phải là nói chung một cách tốt để vượt qua các thông số xung quanh - thành phần chức năng thường tốt hơn trong thời gian dài. Lý do cho việc này là binding tạo ra một ngữ cảnh tiềm ẩn cần thiết để thực hiện chức năng của bạn và điều này có thể khó kiểm tra/gỡ lỗi.
+0

Tôi hiểu ưu/nhược điểm của ràng buộc. Tôi cũng nhận ra ví dụ mã đầu tiên là mã clojure bất thường. Những gì tôi không hiểu là tại sao nó không hoạt động (với 1,3, repl tươi). – Kevin

+0

Tôi gặp khó khăn khi nhìn thấy khi nào bạn muốn ràng buộc! Nó trông anathema đến cách chức năng. Tôi đang thiếu gì? – Hendekagon

+1

@Hendekagon - có thể xứng đáng là một câu hỏi SO theo đúng nghĩa của nó. Nhưng tôi đã thấy nó hữu ích như một cách bổ sung để chuyển ngữ cảnh tròn trong khi gỡ rối/làm việc tại REPL - nếu bạn làm điều này theo cách hoàn toàn chức năng thì bạn sẽ cần phải tạo các tham số mới trong suốt một cuộc gọi (có khả năng rất dài) đồ thị. – mikera

Các vấn đề liên quan