2010-07-06 14 views
11

Todo, estoy empezando a echar un vistazo al lenguaje Clojure, y tenía un par de preguntas sobre algo que estoy tratando de hacer. El objetivo general es alias la función de secuencia every? a all?. Estoy seguro de que hay una función o macro que hace alias-ing (o algo parecido), pero quería ver si era posible con algunas de las construcciones básicas que conozco hasta ahora. Mi enfoque iba a ser definir una función llamada all? que aplica sus argumentos a la implementación every?.Pregunta de metaprogramación Clojure (para un principiante)

Tengo curiosidad por ver si esto puede hacerse agnóstico, así que quería parametrizar mi función de alias para tomar dos argumentos, el nuevo nombre (como Palabra clave) y el nombre anterior (como una referencia de función). Al esforzarme por alcanzar este objetivo, me he encontrado con dos problemas.

1) Definición de funciones nombradas con palabras clave arroja errores. Aparentemente quiere clojure.lang.IObj.

user=> (defn :foo "bar")  
java.lang.ClassCastException: clojure.lang.Keyword cannot be cast to clojure.lang.IObj (NO_SOURCE_FILE:0) 

¿Hay una función para emitir una palabra clave a un Iobj, u otros medios para parametrizar el nombre de una función que acaba de definir con algún valor proporcionado? (En Ruby, define_method entre otras técnicas hace esto)

irb(main)> self.class.instance_eval do 
irb(main)* define_method(:foo) { "bar" } 
irb(main)> end 
=> #<Proc> 
irb(main)> foo 
=> "bar" 

2) Recoger todos los argumentos a una función en una sola variable. Incluso funciones básicas como (+ 1 2 3 4) toman una cantidad variable de argumentos. Todas las técnicas de definición de funciones que he visto hasta ahora toman una cantidad específica de argumentos, sin posibilidad de agregar todo en una lista para su manejo en el cuerpo de la función. Una vez más, lo que voy a hacer se hace en Ruby así:

irb(main)> def foo(*args) 
irb(main)> p args 
irb(main)> end 
=> nil 
irb(main)> foo(1, 2, 3) 
[1, 2, 3] 
=> nil 

¡Gracias por cualquier ayuda que me puedan dar!

Respuesta

17

voy a responder en forma de puntos, ya que las preguntas se pueden dividir perfectamente en una serie de cuestiones separadas.

  • Algo que está contenida implícitamente en lo que sigue, pero que tal vez merece una bala de su propia: los objetos de nivel superior creados por def & Co. (y en particular por defn) son Vars. Entonces, lo que realmente quiere hacer es alias un Var; las funciones son solo valores regulares que realmente no tienen nombres (excepto que pueden tener un nombre vinculado localmente dentro de sus cuerpos, sin embargo, eso no tiene nada que ver con el problema).

  • No es de hecho un "macro aliasing" disponible en Clojure - clojure.contrib.def/defalias:

    (use '[clojure.contrib.def :only [defalias]]) 
    (defalias foo bar) 
    ; => foo can now be used in place of bar 
    

    La ventaja de este sobre (def foo bar) es que se copia sobre metadatos (tales como la cadena de documentación); incluso parece funcionar con macros en HEAD actual, aunque recuerdo un error que impedía eso en versiones anteriores.

  • Los Vars se nombran por símbolos, no palabras clave.Los literales de símbolo en Clojure (y otros Lisps) no comienzan con dos puntos (:foo es una palabra clave, no un símbolo). Así, para definir una función llamada foo debe escribir

    (defn foo [...] ...) 
    
  • defn es una macro ayudante de facilitar la creación de nuevos la función de retención de Vars, al permitir al programador utilizar una mezcla de def & fn sintaxis. Por lo tanto, defn está fuera de lugar para crear Vars con valores preexistentes (que pueden ser funciones), como se requiere para crear alias; use defalias o simplemente def en su lugar.

  • Para crear una función variadic, utilice la siguiente sintaxis:

    (fn [x y & args] ...) 
    

    x y y se requerirá argumentos posicionales; el resto de los argumentos pasados ​​a la función (cualquier número de ellos) se recopilarán en una secuencia y estarán disponibles con el nombre args. No tiene que especificar ningún "argumento posicional requerido" si no se necesitan: (fn [& args] ...).

    Para crear un VAR que ocupen una función variadic, utilice

    (defn foo [x y & args] ...) 
    
  • Para aplicar una función a algunos argumentos que tienes montado en un objeto seqable (como el args siguientes en los ejemplos anteriores o tal vez un vector & c), utilice apply:

    (defn all? [& args] 
        (apply every? args)) 
    
  • Si desea escribir una funciónpara crear alias - en contraposición a. una macro - necesitará investigar las funciones intern, with-meta, meta - y posiblemente resolve/ns-resolve, dependiendo de si la función es aceptar símbolos o Vars. Dejaré de llenar los detalles como un ejercicio para el lector. :-)

+1

Acabo de publicar una Gist con una función 'intern-alias' realizando el diseño al que se aludía en el último párrafo. Agregaré una docstring en un segundo. Copia el meta del original al alias, admite la creación de alias en espacios de nombres distintos al actual, acepta tanto símbolos como Vars como los originales & c. Ver http://gist.github.com/464970 –

+0

Vamos, amigo. ¡Al menos dale a otras personas una oportunidad! ¿No hay algún tipo de medicamento que pueda tomar para reducir su nivel de inteligencia a * normal *? Vuelve con nosotros, hermano, te extrañamos. : p – Rayne

+0

Gracias por el comentario en profundidad. Definitivamente iluminó la mayoría de todo lo que estaba buscando. Con respecto al punto n. ° 3 (los vars se nombran por símbolos, no por palabras clave), ¿hay alguna manera de incluir una palabra clave en un símbolo? Digamos, por el bien de un ejercicio, quería escribir una función que incluyera def, y tomé dos argumentos, una palabra clave y un valor, y luego llamé internamente def y llamé a la variable el nombre de la palabra clave. ¿Posible? –

6

¿Lo único que tiene que hacer es atar todo? función para el todo? símbolo, que se realiza a través de def:

(def all? every?) 

Por un poco más sobre esto, ver Clojure macro to create a synonym for a function

+0

I estaba explorando la implementación de una función como esta, pero me alegra saber la forma rápida de hacerlo, ¡gracias! –

3

No creo que pueda añadir mucho más a las explicaciones existentes aquí, excepto tal vez llenar un par de espacios en blanco en el diccionario de los viajeros en Ruby on colección de argumentos y desestructuración:

(defn foo [& args]     ; Ruby: def foo(*args) 
    (println args))  
user=> (foo 1 2 3) 
(1 2 3) 

(defn foo [& args] 
    (+ args)) 
user=> (foo 1 2 3) 
java.lang.ClassCastException  ; + takes numbers, not a list 

(defn foo [& args] 
    (apply + args))     ; apply: as Ruby proc.call(*args) 
user=> (foo 1 2 3) 
6 

(defn foo [& args] 
    (let [[a b & other] args]  ; Ruby: a, b, *other = args 
    (println a b other))) 
user=> (foo 1 2 3) 
1 2 (3) 
+0

Gracias por los sinónimos. :] –

Cuestiones relacionadas