2010-07-28 11 views
8

Peinlicherweise habe ich Probleme, dieses Makro richtig zu entwerfen. Probleme mit diesem Makro

Dies ist das Makro wie ich es geschrieben:

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

Es muss in zwei Sammlungen oder ASTA nehmen, xs und ys, und ich brauche es mir zu geben ...

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

... für xs = [0 1 3 5 7] und ys = [1 1 3 6 8].

Dies funktioniert gut, wenn ich meine Makro Ebene ‚n‘ einfache Vektoren geben (z [1 2 3 4] und [2 3 4 5]) aber funktioniert nicht, wenn ich ihm ein faules-Seq/alles dafür geben, die wie (take 16 (iterate #(+ 0.1 %1) 0)) und (take 16 (cycle [0 -0.1 0 0.1])))) ausgewertet werden muss.

Ich weiß, dass dies daran liegt, dass diese auf das Makro unevaluated übergeben werden, und so bekomme ich zum Beispiel (vertex take take) als mein erstes Ergebnis (glaube ich). Leider hat alles, was ich versucht habe, diese zuerst zu bewerten und dann mein Makro-Rewriting auszuführen, gescheitert/schrecklich hacky ausgesehen.

Ich bin sicher, dass ich hier ein paar grundlegende Syntax-Zitat/Anführungsstrich-Muster vermisse-ich würde einige Hilfe/Zeiger lieben!

Vielen Dank.

EDIT ich erwähnen sollte, ist draw-line-strip ein Makro, und vertex schafft eine OpenGL Vertex; sie sind beide Teil der Penumbra Clojure+OpenGL library.

EDIT 2 Dies ist für ein benutzerdefiniertes Grafik-Tool, das ich brauche, und die primäre Motivation für die Erstellung war es, schneller zu sein als JFreeCharts und Unternehmen.

EDIT 3 Ich nehme an sollte ich anmerken, dass ich eine Makro-Version Arbeits tun, es ist einfach schrecklich und Hacky, wie ich oben erwähnt. Es verwendet eval, wie unten gezeigt, aber wie folgt aus:

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

Leider bekomme ich ...

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

... wenn dies mit ein paar tausend Stück lange Liste mit (s). Dies liegt daran, dass ich viel zu viel in den vorkompilierten Code schreibe, und die Klassendatei kann (so wie ich denke) nicht so viele Daten/Codes verarbeiten. Es sieht so aus, als müsste ich irgendwie eine Funktionsversion von draw-line-strip erhalten, wie es vorgeschlagen wurde.

Ich bin jedoch offen für eine elegantere, weniger hackische, Makro-Lösung für dieses Problem. Wenn einer existiert!

+0

warum schreiben Sie stattdessen eine Funktion? –

+0

Denn soweit ich weiß, ist das nicht möglich! Ich habe diese 'xs'- und' ys'-Arrays, und ich muss sie in einem gewissen Sinne innerhalb eines 'draw-line-strip' neu formatieren und in' vertex'-Symbole einschließen. Ich bin auch daran interessiert, dies mit einem Makro zu tun, obwohl ich mehr als glücklich wäre, eine Lösung zu sehen, die auch eine Funktion verwendet. – Isaac

+2

Eigentlich ist es nicht nur möglich, es als eine Funktion zu schreiben (siehe Dev-Dev-Antwort), aber es ist tatsächlich unmöglich, es als ein Makro zu schreiben und es mit arbiträren Seq-Argumenten arbeiten zu lassen (beachten Sie, dass die Expansion des Makros vollständig bestimmt werden muss) wenn der Code kompiliert ist). –

Antwort

4

Ich schaute auf die Makro-Erweiterung für Draw-Line-Streifen und bemerkte, dass es nur den Körper in eine Bindung, Gl-Anfang und Gl-Ende wickelt. Sie können also den gewünschten Code in das Programm einfügen.

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

sollte also funktionieren.

+0

Das habe ich nicht versucht ... die Erweiterung anzuschauen. Daran habe ich nicht einmal gedacht! Vielen Dank. – Isaac

+1

Sie sind herzlich willkommen. Ich bemerkte gerade die unnötige Verwendung von # (...% 1% 2) auch, also besser noch: (defn Konstrukt-Vertices [xs ys] (Zeichnungslinie-Streifen (dorun (Karte Vertex xs ys)))) – dreish

2

Warum nicht so etwas, Funktion anstelle von Makro weiter:

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

Das nennt mit dem erforderlichen args ziehen-line-Streifen soll. Dieses Beispiel eignet sich nicht für Makros, die nicht für Funktionen verwendet werden sollten.

Hinweis: Ich habe es nicht versucht, da ich keinen Schleim auf dieser Box eingerichtet habe.

EDIT: Ich schaue wieder, ich weiß nicht, ob Sie Scheitelpunkt vor Aufruf von Draw-Line-Strip auswerten möchten. In diesem Fall wird Funktion wie folgt aussehen:

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

Ich werde auch hier kommentieren: 'draw-line-strip' ist ein Makro, also' apply' funktioniert nicht damit. Sonst würde ich es gerne benutzen! – Isaac

2

Wenn Sie wirklich draw-line-strip benötigen, was ein Makro und Sie eine vollständig allgemeine Methode zu tun, die Frage Text beschreibt und Sie nicht zu viel Pflege

(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 

Beachten Sie, dass diese schreckliche Art ist, es sei denn es wirklich notwendig ist: über ein bisschen ein Performance-Einbußen, könnten Sie eval verwenden.

+0

Beachten Sie, dass das Obige eine Funktion und kein Makro ist und somit die Laufzeitwerte 'xs' und' ys' zur Verfügung stehen. Diese werden dann verwendet, um eine "Draw-Line-Strip" -Form zu konstruieren, die zur Laufzeit macroexpandiert und kompiliert wird. Es gibt natürlich einen gewissen Leistungsaufwand, aber es ist wirklich der einzige Weg, um das zu tun, was die Frage verlangt, außer dass man "draw-line-strip" umschreibt, um kein Makro zu sein (welches, wenn möglich, wahrscheinlich das sein würde sauberste Lösung). –

+0

Muss der Vertex nicht syntaktisch zitiert werden? Edit: ah, nein, da es Evals in den gleichen ns ist. –

1

Das sieht wie ein typisches Problem mit einigen der Makrosysteme in Lisp aus. Es gilt die übliche Lisp-Literatur. Zum Beispiel On Lisp von Paul Graham (verwendet Common Lisp).

Normalerweise verwendet ein Makro die Quelle, die es umgibt, und erzeugt eine neue Quelle. Wenn ein Makroaufruf (foo bar) ist und das Makro basierend auf dem Wert von bar etwas anderes erzeugen soll, ist dies im Allgemeinen nicht möglich, da der Wert von bar im Allgemeinen für den Compiler nicht verfügbar ist. BAR hat zur Laufzeit nur einen Wert, nicht wenn der Compiler die Makros erweitert. Man müsste also zur Laufzeit den richtigen Code generieren - was zwar möglich ist, aber meist als schlechter Stil angesehen wird.

In diesen Makrosystemen können Makros nicht angewendet werden. Eine typische Lösung sieht wie folgt aus (Common Lisp):

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

Es ist nicht immer möglich ist, über-Lösung zu verwenden, though. Zum Beispiel wenn die Anzahl der Argumente variiert.

Es ist auch keine gute Idee, dass DRAW-LINE-STRIP ein Makro ist. Es sollte besser als eine Funktion geschrieben werden.