2011-08-06 16 views
5

estoy tratando de traducir la siguiente macro de tierra de Lisp en clojure:argumentos de la macro en clojure

(defmacro tag (name atts &body body) 
    `(progn (print-tag ',name 
        (list ,@(mapcar (lambda (x) 
             `(cons ',(car x) ,(cdr x))) 
            (pairs atts))) 
        nil) 
      ,@body 
      (print-tag ',name nil t))) 

pero no dejo quedarse con atts requiere 1 nivel más de la evaluación. P.ej. las siguientes necesidades para evaluar t #:

(defmacro tag [tname atts & body] 
    `(do (print-tag '~tname '[[email protected](map (fn [[h# t#]] [h# t#]) (pair atts))] nil) 
    [email protected] 
    (print-tag '~tname nil true))) 

ya que produce cosas como:

(tag mytag [color 'blue size 'big]) 
<mytag color="(quote blue)" size="(quote big)"><\mytag> 

Donde quiero el atributo a evaluar. Si utilizo "(eval t #)" en el cuadro anterior, me quedan problemas como este:

(defn mytag [col] (tag mytag [colour col])) 
java.lang.UnsupportedOperationException: Can't eval locals (NO_SOURCE_FILE:1) 

¿Alguna sugerencia?

¿Por qué parece que se produce un menor nivel de evaluación en Clojure?

Las definiciones de las funciones de apoyo:

;note doesn't handle nils because I'm dumb 
(defn pair [init-lst] 
     (loop [lst init-lst item nil pairs []] 
    (if (empty? lst) 
     pairs 
     (if item 
     (recur (rest lst) nil (conj pairs [item (first lst)])) 
     (recur (rest lst) (first lst) pairs))))) 

(defn print-tag [name alst closing] 
     (print "<") 
     (when closing 
    (print "\\")) 
     (print name) 
     (doall 
     (map (fn [[h t]] 
      (printf " %s=\"%s\"" h t)) 
     alst)) 
     (print ">")) 

(Por alguna razón que no hice la función par de la misma manera como el libro lo que significa que no maneja correctamente Nils)

+0

Cómo están '' pair' y de impresión-tag' definido? –

+0

@Brian He agregado estas definiciones a la publicación –

+0

me parece que 'pair' es lo mismo que' (partition 2 seq) ', ¿no? – skuro

Respuesta

4

Su definición de Clojure de tag cita todo en el mapa de atributos, mientras que la versión de lisp común solo cita los nombres. Esa es la fuente inmediata de tus problemas: si solo dejaste caer el ' delante de tu vector/mapa, y luego jugabas con el map para citar el primer elemento, probablemente estarías bien.

Sin embargo, aunque portar puede ser un buen ejercicio, este código no está escrito en The Clojure Way: la impresión es un efecto secundario desagradable que hace que sea difícil usar la etiqueta de impresión para hacer algo significativo; devolver una cadena sería mucho más agradable.

(defmacro tag [name attrs & body] 
    `(str "<" 
     (clojure.string/join " " 
          ['~name 
           [email protected](for [[name val] (partition 2 attrs)] 
            `(str '~name "=\"" ~val "\""))]) 
     ">" 
     [email protected] 
     "</" '~name ">")) 

user> (tag head [foo (+ 1 2)] "TEST" (tag sample [])) 
"<head foo=\"3\">TEST<sample></sample></head>" 

Por supuesto, dado que el orden no importa, utilizar un mapa en lugar de un vector es más agradable para los atributos. Eso también significaría que podría soltar el (partition 2...), ya que una vista secuencial de un mapa sale como pares ya.

Y una vez que hemos llegado hasta aquí, resulta que ya hay muchas formas de representar XML como estructuras de datos de Clojure, por lo que nunca usaría mi código anterior en una aplicación real. Si desea hacer XML de verdad, consulte cualquiera de Hiccup, prxml o data.xml.

+0

Sí, acepto que sería mucho mejor usar un mapa. También usar palabras clave como sugiere Hamza Yerlikaya sería una mejora. No estoy convencido de devolver una cadena; preferiría que se imprimiera directamente en una transmisión (aunque el uso de la impresión tampoco es el correcto). Sin embargo, al final solo intentaba comprender las macros de clojure y cómo se comparaban con el ceceo. –

+0

Gracias por esos enlaces por cierto. prxml parece ser este enfoque hecho correctamente. Una de las cosas buenas de clojure es que es fácil de leer y aprender del código de otras personas. –

+0

No estoy seguro acerca de "adecuado" - prxml es la más afectada por mis sugerencias, y como parte de contrib anterior está en desuso. data.xml es el futuro y debe contener todas las características de prxml en un paquete más flexible. – amalloy

0

I podría faltar algo pero hay una razón particular por la que citó azul y grande pero no color y tamaño, también citó en la macro el vector para que no se evalúen cosas dentro de él, si suelta la cita alrededor del vector y también cita color y grande obtienes lo que quieres,

 

(defmacro tag [tname atts & body] 
    `(do (print-tag '~tname [[email protected](map (fn [[h# t#]] [h# t#]) (pair atts))] nil) 
     [email protected] 
     (print-tag '~tname nil true))) 

(tag mytag ['color 'blue 'size 'big]) 
 
<mytag color="blue" size="big"><\mytag>nil 

Solo para el registro en lugar de símbolos usando palabras clave sería más idiomático clojure para esto.

+0

La idea es que los nombres de los atributos rara vez se calculan, pero sus valores son, p. (etiqueta rectángulo [altura (+ 3 4)]). No requerir la cita antes del nombre del atributo es solo un poco más conciso. –

0

En aras de la exhaustividad, lo que quería resultó ser:

(defmacro tag [tname atts & body] 
    `(do (print-tag '~tname [[email protected](map (fn [[h# t#]] [`'~h# t#]) (pair atts))] nil) 
    [email protected] 
    (print-tag '~tname nil true))) 
+0

Eso no se compilará, así que lo dudo. Su truco '\' '~ 'pertenece a la * segunda * instancia de' h # ', no al primero. – amalloy

+0

Es cierto. Eso es raro, pensé que lo corté y pegué ... Corregido. –

Cuestiones relacionadas