2012-10-08 23 views
8

Tôi nhận thấy đây là một ý tưởng tồi vì nhiều lý do. Tôi cũng nhận ra rằng với tôi có một rep stackoverflow của 23, đó là bản chất để giả định rằng tôi là một newb học chương trình. Tuy nhiên, hãy làm hài lòng tôi, và tập trung vào "làm thế nào chúng ta có thể làm điều này" hơn là "tại sao bạn muốn làm điều này/bạn không muốn làm điều này" khía cạnh.Clojure Dynamic Binding

Những gì tôi muốn:

(def dog (Dog. ...)) 
(def cat (Cat. ...)) 

(with-animal dog 
    (println (str "Dog: " (speak) "\n"))) 
(with-animal cat 
    (println (str "Cat: " (speak) "\n"))) 

để đầu ra:

Dog: woof 
Cat: meow 

Vì vậy, về cơ bản, tôi muốn với động vật là một s.t. vĩ mô tất cả các lần xuất hiện của cuộc gọi hàm "nói" được ánh xạ tới đối tượng mà tôi đang gọi khối.

Đặc biệt, tôi không muốn viết:

(let-binding [speak (fn [] "woof")] ...) 
(let-binding [speak (fn [] "meow")] ...) 

Thay vào đó, tôi muốn với động vật để làm cho nói bản đồ chức năng để một số phương pháp của đối tượng Tôi gọi với.

Có cách nào để làm điều này trong Clojure không?

Cảm ơn!

+1

Tại sao bạn không sử dụng giao thức? – DanLebrero

+0

tôi thích tuyên bố từ chối trách nhiệm :) – szymanowski

Trả lời

20

Gắn kết động tồn tại vì một lý do và nó có nhiều công dụng tuyệt vời, vì vậy đừng lo lắng về việc bị quấy rầy vì tìm cách hiểu nó :-) Có một số nhầm lẫn nổi xung quanh nhiều hướng dẫn cũ hơn về Clojure. ^: siêu dữ liệu động cho các vars mà bạn mong muốn tự động rebind.

Ví dụ đầu tiên này sử dụng liên kết động bằng cách đặt lại tên hiện có. Điều này loại bỏ sự cần thiết của các vĩ mô để giới thiệu một biểu tượng mới:


đầu tiên thực hiện một số động vật, tôi chỉ sử dụng bản đồ trong ví dụ này, nhiều người sẽ sử dụng một số loại khác của đối tượng:

(def dog {:sound #(str "wooooof")}) 
(def cat {:sound #(str "mewwww")}) 

xác định chức năng chúng tôi sẽ được rebinding phải năng động (cho phép rebinding)

(defn :^dynamic speak [] (println "eh?")) 

viết một mẫu vĩ mô cơ bản để ràng buộc nói chuyện với các chức năng trong các động vật:

(defmacro with-animal [animal & body] 
    `(binding [speak (:sound ~animal)] 
     [email protected])) 

và thử nghiệm nó:

(with-animal dog 
    (println (str "Dog: " (speak) "\n"))) 
Dog: wooooof             


và bây giờ là "phiên bản tiên tiến" mà chỉ giới thiệu một biểu tượng speak vào phạm vi sử dụng một let mà không cần cho động ràng buộc.Đây không phải là để nói rằng ràng buộc là xấu trong một số cách, nó chỉ phù hợp hơn với mong muốn của bạn để không viết (let-binding [speak (fn [] "meow")] ...) Loại maco này được gọi là anaphoric (nếu bạn thích những cái tên lạ mắt):

phần quan trọng là các ~' trước speak biểu tượng mà giới thiệu một cách rõ ràng một biểu tượng un-có trình độ vào phạm vi:

user> (defmacro with-animal [animal & body] 
    `(let [~'speak (:sound ~animal)] 
     [email protected])) 
#'user/with-animal 

user> (with-animal dog 
     (println (str "Dog: " (speak) "\n"))) 
Dog: wooooof 

nil 


tôi hy vọng rằng sự tương phản giữa hai ví dụ này dùng để trả lời câu hỏi của bạn về ràng buộc hành vi từ một đối tượng vào một phạm vi . Ví dụ đầu tiên liên kết giá trị cho phần thân của maco VÀ bất cứ thứ gì được gọi từ cơ thể đó. Ví dụ thứ hai giới thiệu tên CHỈ cho phần thân của macro.

+0

Tôi thích giải pháp này. Tôi có thể thấy bản thân mình có một nơi xác định tất cả các chức năng sử dụng động vật, sau đó cho mỗi con vật, xác định nó trong tập tin đó. –

+1

'speak' không cần phải là một hàm trong một trong hai ví dụ, phải không? Nó có thể chỉ là '(def ^: dynamic speak" eh? ")'. Tất cả những gì bạn đang làm là ghép nó vào một chuỗi. – Ben

0

Nếu bạn thực sự muốn làm cho các loại động vật nói chuyện idiomatically, sử dụng giao thức Clojure:

(defprotocol ISpeak 
    (speak [animal] "make the type say it's thing")) 

(deftype Dog [] 
    ISpeak 
    (speak [this] "Woof!")) 

(deftype Cat [] 
    ISpeak 
    (speak [_] "Meow!!")) ;;you can "drop" the item if not used using _ 

(def a-dog (Dog.)) 
(speak a-dog) 
;;=>"Woof!" 

(def a-cat (Cat.)) 
(speak a-cat) 
;;=>"Meow!!" 

Xin lưu ý bạn có thể mở rộng bất kỳ loại (lớp) với phương pháp nói chuyện.

(extend java.util.Random 
    ISpeak 
    {:speak (fn [_] "I'm throwing dices at you!")}) 

(speak (java.util.Random.)) 
;;=>"I'm throwing dices at you!" 

Cú pháp hơi khác với các lớp Java, vui lòng xem Protocols documentation để biết thêm thông tin.