2012-10-08 16 views
8

Me doy cuenta de que la siguiente es una mala idea por muchas razones. También me doy cuenta de que dado que tengo un representante de stackoverflow de 23, es natural suponer que soy un principiante en aprender a programar. Sin embargo, por favor, cuéntame, y concéntrate en el aspecto "¿cómo podemos hacer esto?" En lugar de "¿por qué quieres hacer esto/no quieres hacer esto?".Clojure Dynamic Binding

lo que quiero:

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

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

a salida:

Dog: woof 
Cat: meow 

Así que, básicamente, quiero con animales ser un macro S.T. todas las ocurrencias de la llamada de función "hablar" se asignan al objeto con el que estoy llamando al bloque.

En particular, no quiero escribir:

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

Más bien, quiero que el con-animal para hacer el hablan mapa de funciones a algún método del objeto que estoy llamando a.

¿Hay alguna manera de hacerlo en Clojure?

Gracias!

+1

¿Por qué no utiliza protocolos? – DanLebrero

+0

me gustó la exención de responsabilidad :) – szymanowski

Respuesta

20

La vinculación dinámica existe por una razón y tiene muchos usos geniales, por lo que no se preocupe por ser enardecida por tratar de entenderlo :-) Existe una confusión que flota en torno a muchos tutoriales antiguos de Clojure que son anteriores a la necesidad de agregar ^: metadatos dinámicos para vars que espera volver a enlazar dinámicamente.

Este primer ejemplo utiliza un enlace dinámico volviendo a enlazar un nombre existente. Esto elimina la necesidad de que la macro para introducir un nuevo símbolo:


En primer lugar hacer algunos animales, sólo tiene que utilizar los mapas en este ejemplo, mucha gente va a utilizar algún otro tipo de objeto:

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

definir la función que será reconsolidación ser dinámica (que permite reconsolidación)

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

escribir una macro plantilla básica de obligar a hablar a la función en el animal:

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

y probarlo:

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


y ahora la versión "avanzada" que acaba introduce un símbolo speak en el ámbito de aplicación usando un let sin necesidad de unión dinámica.Esto no quiere decir que el enlace sea malo de alguna manera, simplemente se ajusta más a su deseo de no escribir (let-binding [speak (fn [] "meow")] ...) Este tipo de maco se llama anafórico (si le gustan los nombres sofisticados):

la parte importante es la ~' antes del símbolo speak que introduce explícitamente un símbolo no-cualificado en el ámbito de aplicación:

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 


espero que el contraste entre estos dos ejemplos sirve para responder a su pregunta sobre el comportamiento de la unión de un objeto en un ámbito . El primer ejemplo vincula el valor para el cuerpo del maco Y cualquier cosa que se llame desde ese cuerpo. El segundo ejemplo introduce el nombre SÓLO para el cuerpo de la macro.

+0

Me gusta esta solución. Puedo verme a mí mismo teniendo un lugar que define todas las funciones que usa el animal, luego para cada animal, definiéndolo en ese archivo. –

+1

'speak' no necesita ser una función en ninguno de los ejemplos, ¿verdad? Podría ser simplemente '(def ^: dynamic speak" ¿eh? ")'. Todo lo que estás haciendo es concatenarlo en una cadena de todos modos. – Ben

0

Si realmente quiere hacer tipos de animales hablan idiomáticamente, utilizar protocolos de 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!!" 

Tenga en cuenta que puede extender cualquier tipo (clase) con el método de hablar.

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

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

La sintaxis es un poco diferente para las clases de Java, consulte la Protocols documentation para más información.