2010-07-28 20 views
8

Avergonzosamente, estoy teniendo problemas para diseñar correctamente esta macro.Problema con esta macro

Esta es la macro como lo tengo escrito:

(defmacro construct-vertices 
    [xs ys] 
    (cons 'draw-line-strip 
     (map #(list vertex %1 %2) xs ys))) 

Se tiene que tomar en dos colecciones o SEQs, xs y ys, y necesito que me dé ...

(draw-line-strip (vertex 0 1) (vertex 1 1) 
       (vertex 3 3) (vertex 5 6) 
       (vertex 7 8)) 

... para xs = [0 1 3 5 7] y ys = [1 1 3 6 8].

Esto funciona bien si le doy mi macro llanura 'n' vectores simples (por ejemplo [1 2 3 4] y [2 3 4 5]) pero no funciona si le doy un perezoso-ss/cualquier cosa que necesita ser evaluado como (take 16 (iterate #(+ 0.1 %1) 0)) y (take 16 (cycle [0 -0.1 0 0.1])))).

Me doy cuenta de que esto se debe a que se transfieren a la macro sin evaluar, por lo que obtengo, por ejemplo, (vertex take take) como primer resultado (creo). Desafortunadamente, todo lo que he intentado primero para evaluar estos y luego llevar a cabo mi macro-reescritura ha fallado/se veía terriblemente hacky.

Estoy seguro de que me falta algún tipo de patrón básico de sintaxis-comillas/comillas aquí-¡Me encantaría alguna ayuda/consejos!

Muchas gracias.

EDITAR Debo mencionar, draw-line-strip es una macro y vertex crea un vértice OpenGL; ambos son parte de Penumbra Clojure+OpenGL library.

EDIT 2 Esto es para una herramienta gráfica personalizada que necesito, y la motivación principal para crearla era ser más rápido que JFreeCharts y compañía.

EDITAR 3 supongo que debería señalar que hacer tienen una versión de trabajo macro, es simplemente horrible y hacky como he mencionado anteriormente. Utiliza eval, como se demuestra a continuación, pero como esto:

(defmacro construct-vertices 
    [xs ys] 
    (cons 'draw-line-strip 
     (map #(list vertex %1 %2) (eval xs) (eval ys)))) 

Por desgracia, me sale ...

error: java.lang.ClassFormatError: Invalid this class index 3171 in constant pool in class file tl/core$draw_l$fn__9357 (core.clj:14)

... al usar esto con unos pocos miles-elemento de la lista (s) de largo. Esto se debe a que estoy escribiendo demasiado en el código precompilado, y el archivo de clase no puede manejar (supongo) tantos datos/códigos. Parece que necesito, de alguna manera, obtener una versión de función de draw-line-strip, como se ha sugerido.

Todavía estoy abierto, sin embargo, a una solución macro más elegante, menos hackosa, para este problema. ¡Si existe uno!

+0

¿por qué no escribir una función en su lugar? –

+0

Porque, hasta donde sé, ¡eso no es posible! Tengo estas matrices 'xs' y' ys', y necesito reformatearlas, en cierto sentido, dentro de una 'draw-line-strip', y envueltas en' vertx'es. También estoy interesado en hacer esto con una macro, aunque estaría más que feliz de ver una solución usando una función también. – Isaac

+2

En realidad, no solo es posible escribirlo como una función (ver la respuesta de Dev er dev), sino que es imposible escribirlo como una macro y hacer que funcione con argumentos seq arbitrarios (considere que la expansión de la macro debe estar completamente determinada cuando se compila el código). –

Respuesta

4

Miré la macro expansión para dibujar la tira de línea y noté que simplemente envuelve el cuerpo en un enlace, gl-begin y gl-end. Entonces puedes poner el código que quieras dentro.

Así

(defn construct-vertices [xs ys] 
    (draw-line-strip 
    (dorun (map #(vertex %1 %2) xs ys)))) 

debería funcionar.

+0

Eso es algo que no probé ... mirando la expansión. ¡Ni siquiera pensé en eso! Muchas gracias. – Isaac

+1

Eres bienvenido. Acabo de notar el uso innecesario de # (...% 1% 2) también, así que mejor aún: (defn construc-vertices [xs ys] (draw-line-strip (dorun (map vertex xs ys)))) – dreish

2

Por qué no algo como esto, el uso de la función en lugar de macro:

(defn construct-vertices [xs ys] 
    (apply draw-line-strip (map #(list vertex %1 %2) xs ys))) 

que deben llamar a llamar la línea-tira con argumentos necesarios. Este ejemplo no es el más adecuado para las macros, que no deberían usarse donde las funciones pueden funcionar.

Nota: No lo intenté ya que no tengo configurado slime en esta caja.

EDITAR: Mirando nuevamente, no sé si desea evaluar el vértice antes de llamar a draw-line-strip. En ese caso, la función se verá así:

(defn construct-vertices [xs ys] 
    (apply draw-line-strip (map #(vertex %1 %2) xs ys))) 
+0

Voy a comentar aquí también: 'draw-line-strip' es una macro, por lo tanto' apply' no funciona con ella. De lo contrario, estaría feliz de usarlo! – Isaac

2

Si realmente necesita draw-line-strip a ser una macro y desea un método completamente general de hacer lo que el texto de la pregunta describe y no les importa demasiado alrededor de un poco de un impacto en el rendimiento, podría utilizar eval:

(defn construct-vertices [xs ys] 
    (eval `(draw-line-strip [email protected](map #(list 'vertex %1 %2) xs ys)))) 
             ; ^- not sure what vertex is 
             ; and thus whether you need this quote 

Tenga en cuenta que esto es terrible estilo a menos que sea realmente necesario.

+0

Tenga en cuenta que lo anterior es una función, no una macro, y por lo tanto tiene los valores de tiempo de ejecución de 'xs' y' ys' disponibles. Estos se utilizan para construir un formulario 'draw-line-strip' que se macroexpandió y compiló en tiempo de ejecución. Por supuesto, esto tiene un cierto costo de rendimiento, pero en realidad es la única forma de hacer lo que la pregunta pide, salvo la reescritura de 'draw-line-strip' para no ser una macro (que, de ser posible, probablemente sería la la solución más limpia). –

+0

¿No es necesario que los vértices sean citados con sintaxis? Editar: ah, no, ya que está recibiendo evaluaciones en el mismo ns. –

1

Parece un problema típico con algunos de los macro sistemas en Lisp. La literatura habitual de Lisp se aplica. Por ejemplo, On Lisp por Paul Graham (usa Common Lisp).

Por lo general, una macro utiliza la fuente que incluye y genera una nueva fuente. Si una macro llamada es (foo bar) y la macro debe generar algo diferente en función del valor de la barra, esto generalmente no es posible, ya que el valor de la barra generalmente no está disponible para el compilador. BAR realmente tiene solo un valor en tiempo de ejecución, no cuando el compilador expande las macros. Entonces uno necesitaría generar el código correcto en tiempo de ejecución, lo que podría ser posible, pero que generalmente se considera como un estilo malo.

En estos macro sistemas no se pueden aplicar macros. Una solución típica se parece a esto (Common Lisp):

(apply (lambda (a b c) 
      (a-macro-with-three-args a b c)) 
     list-of-three-elements) 

No siempre es posible utilizar la solución anterior, sin embargo. Por ejemplo, cuando la cantidad de argumentos varía.

Tampoco es una buena idea que DRAW-LINE-STRIP sea una macro. Debería escribirse mejor como una función.

Cuestiones relacionadas