Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trouble with this macro

Embarrassingly enough, I'm having some trouble designing this macro correctly.

This is the macro as I have it written:

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

It needs to take in two collections or seqs, xs and ys, and I need it to give me…

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

…for xs = [0 1 3 5 7] and ys = [1 1 3 6 8].

This works just fine if I give my macro plain 'n' simple vectors (e.g. [1 2 3 4] and [2 3 4 5]) but doesn't work if I give it a lazy-seq/anything that needs to be evaluated like (take 16 (iterate #(+ 0.1 %1) 0)) and (take 16 (cycle [0 -0.1 0 0.1])))).

I realize that this is because these are passed to the macro unevaluated, and so I get, for example, (vertex take take) as my first result (I do believe). Unfortunately, everything I've tried to first evaluate these and then carry out my macro-rewriting has failed/looked terribly hacky.

I'm sure I'm missing some sort of basic syntax-quote/unquote pattern here–I'd love some help/pointers!

Thanks so much.

EDIT I should mention, draw-line-strip is a macro, and vertex creates an OpenGL vertex; they are both part of the Penumbra Clojure+OpenGL library.

EDIT 2 This is for a custom graphing tool I need, and the primary motivation for creating it was to be faster than JFreeCharts and company.

EDIT 3 I suppose I should note that I do have a macro version working, it's just horrid and hacky as I mentioned above. It uses eval, as demonstrated below, but like this:

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

Unfortunately, I get…

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

…when using this with a few thousand-item long list(s). This is because I'm writing far too much into the pre-compiled code, and the classfile can't handle (I suppose) that much data/code. It looks like I need to, somehow, obtain a function version of draw-line-strip, as has been suggested.

I'm still open, however, to a more elegant, less hackish, macro solution to this problem. If one exists!

like image 684
Isaac Avatar asked Jul 28 '10 20:07

Isaac


3 Answers

I looked at the macro expansion for draw-line-strip and noticed that it just wraps the body in a binding, gl-begin, and gl-end. So you can put whatever code inside it you want.

So

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

should work.

like image 87
dreish Avatar answered Sep 29 '22 11:09

dreish


Why not something like this, using function instead of macro:

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

That should call draw-line-strip with required args. This example is not the best fit for macros, which shouldn't be used where functions can do.

Note: I didn't try it since I don't have slime set up on this box.

EDIT: Looking again, I don't know if you want to evaluate vertex before calling draw-line-strip. In that case function will look like:

(defn construct-vertices [xs ys]
  (apply draw-line-strip (map #(vertex %1 %2) xs ys)))
like image 41
Marko Avatar answered Sep 29 '22 10:09

Marko


If you really need draw-line-strip to be a macro and you want a fully general method of doing what the question text describes and you don't care too much about a bit of a performance hit, you could use eval:

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

Note that this is terrible style unless it is really necessary.

like image 28
Michał Marczyk Avatar answered Sep 29 '22 10:09

Michał Marczyk