Проблема с этим макросом

Смущающе достаточно я испытываю некоторые затруднения при разработке этого макроса правильно.

Это - макрос, поскольку у меня есть он записанный:

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

Это должно взять в двух наборах или seqs, xs и ys, и мне нужен он, чтобы дать мне …

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

… для xs = [0 1 3 5 7] и ys = [1 1 3 6 8].

Это работает просто великолепно, если я даю свою макро-плоскость 'n' простые векторы (например. [1 2 3 4] и [2 3 4 5]) но не работает, если я даю ему lazy-seq/anything, который должен быть оценен как (take 16 (iterate #(+ 0.1 %1) 0)) и (take 16 (cycle [0 -0.1 0 0.1])))).

Я понимаю, что это вызвано тем, что они передаются макросу, неоцененному, и таким образом, я добираюсь, например, (vertex take take) как мой первый результат (я действительно верю). К сожалению, все, я попытался сначала оценить их и затем выполнить мою макроперезапись, приводило к сбою/смотрело ужасно hacky.

Я уверен, что отсутствую, своего рода основной syntax-quote/unquote шаблон здесь-I'd любят некоторую справку/указатели!

Большое спасибо.

РЕДАКТИРОВАНИЕ я должен упомянуть, draw-line-strip макрос, и vertex создает вершину OpenGL; они - оба часть библиотеки Penumbra Clojure+OpenGL.

ОТРЕДАКТИРУЙТЕ 2, Это для пользовательского инструмента построения графика, в котором я нуждаюсь, и основная мотивация для создания, это должно было быть быстрее, чем JFreeCharts и компания.

ОТРЕДАКТИРУЙТЕ 3, я предполагаю, что должен отметить, что у меня действительно есть макро-работа версии, это просто неприятно и hacky, как я упомянул выше. Это использует eval, как продемонстрировано ниже, но как это:

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

К сожалению, я получаю …

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

… при использовании этого с длинным списком (списками) нескольких-тысяч-объекта. Это вызвано тем, что я пишу слишком много в предварительный скомпилированный код, и classfile не может обработать (я предполагаю), так много данных/кода. Похоже, что я должен, так или иначе, получить функциональную версию draw-line-strip, как был предложен.

Я все еще открыт, однако, к более изящному, меньшему количеству hackish, макро-решению этой проблемы. Если Вы существуете!

8
задан Isaac 29 July 2010 в 14:48
поделиться

4 ответа

Я посмотрел на расширение макроса для draw-line-strip и заметил, что оно просто обертывает тело в привязка, gl-begin и gl-end. Таким образом, вы можете поместить в него любой код, который хотите.

Итак,

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

должно работать.

4
ответ дан 5 December 2019 в 18:55
поделиться

Если вам действительно нужен draw-line-strip в качестве макроса и , вам нужен полностью общий метод выполнения того, что описывает текст вопроса и вы не особо заботьтесь о небольшом снижении производительности, вы можете использовать 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

Обратите внимание, что это ужасный стиль, если он не действительно необходим.

2
ответ дан 5 December 2019 в 18:55
поделиться

Почему бы не сделать что-то вроде этого, используя функцию вместо макроса:

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

Это должно вызвать draw-line-strip с требуемыми args. Этот пример не лучшим образом подходит для макросов, которые не следует использовать там, где это могут сделать функции.

Примечание: я не пробовал, так как у меня нет слима, установленного на этой коробке.

EDIT: Посмотрев еще раз, я не знаю, хотите ли вы оценить вершину перед вызовом draw-line-strip. В таком случае функция будет выглядеть так:

(defn construct-vertices [xs ys]
  (apply draw-line-strip (map #(vertex %1 %2) xs ys)))
2
ответ дан 5 December 2019 в 18:55
поделиться

Это похоже на типичную проблему с некоторыми макросистемами в Lisp. Применяется обычная литература по Лиспу. Например, On Lisp Пола Грэма (использует Common Lisp).

Обычно макрос использует источник, который он заключает в себе, и генерирует новый источник. Если вызов макроса имеет вид (foo bar), и макрос должен генерировать что-то другое, основываясь на значении bar, то это обычно невозможно, поскольку значение bar обычно недоступно компилятору. BAR действительно имеет значение только во время выполнения, а не когда компилятор расширяет макросы. Таким образом, нужно генерировать правильный код во время выполнения - что возможно, но что обычно считается плохим стилем.

В таких макросистемах макросы не могут быть применены. Типичное решение выглядит следующим образом (Common Lisp):

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

Однако не всегда возможно использовать вышеуказанное решение. Например, когда количество аргументов варьируется.

Также не очень хорошо, что DRAW-LINE-STRIP - это макрос. Его лучше записать как функцию.

1
ответ дан 5 December 2019 в 18:55
поделиться