Макрос не работает в функции

У меня проблемы со следующим кодом: http://lisper.ru/apps/format/96

Проблема в функции "нормализовать", которая не работает.
Сбой на пятой строке: (zero-p a indexes i)

(defun normalize (a &optional indexes i)
  "Returns normalized A."
  (progn
   (format t "Data=~A ~A ~A" a indexes i)
   (if (zero-p a indexes i)
      a ;; cannot normalize empty vector
    (let* ((mmm (format t "Zero?=~a" (zero-p a indexes i)))
             (L (sqrt (+ (do-op-on * a :x a :x indexes i indexes i)
                         (do-op-on * a :y a :y indexes i indexes i)
                         (do-op-on * a :z a :z indexes i indexes i))))
             (mmm (format t "L=~a" L))
             (L (/ 1D0 L))
             (mmm (format t "L=~a" L))) ; L=1/length(A)
      (make-V3 (* (ref-of a :x indexes i) l)
                 (* (ref-of a :y indexes i) l)
                 (* (ref-of a :z indexes i) l))))))

в функции «нормализация» я вызываю макрос «zero-p», который, в свою очередь, вызывает макрос «ref-of», который является последним в цепочке.

(defmacro zero-p (v &optional indexes index)
  "Checks if the vector is 'almost' zero length."
  `(and (< (ref-of ,v :x ,indexes ,index) *min+*)
 (< (ref-of ,v :y ,indexes ,index) *min+*)
 (< (ref-of ,v :z ,indexes ,index) *min+*)
 (> (ref-of ,v :x ,indexes ,index) *min-*)
 (> (ref-of ,v :y ,indexes ,index) *min-*)
 (> (ref-of ,v :z ,indexes ,index) *min-*)))

Вот ссылка:

(defmacro ref-of (values coordinate &optional indexes index)
  "Please see DATA STRUCTURE for details."
  (if indexes
    (cond ((eq coordinate :x) `(aref ,values (aref ,indexes ,index)))
   ((eq coordinate :y) `(aref ,values (+ 1 (aref ,indexes ,index))))
   ((eq coordinate :z) `(aref ,values (+ 2 (aref ,indexes ,index))))
   (T (error "The symbol ~S is not :X, :Y or :Z." coordinate)))
    (cond ((eq coordinate :x) `(aref ,values 0))
 ((eq coordinate :y) `(aref ,values 1))
     ((eq coordinate :z) `(aref ,values 2))
     (T (error "The symbol ~S is not :X, :Y or :Z." coordinate)))))

Кроме того, в «нормализации» я вызываю макрос «do-op-on», который также вызывает «ref-of».

(defmacro do-op-on (op name1 coord1 name2 coord2 &optional is1 i1 is2 i2)
  "Example: (do-op-on * A :x B :y i n) == A[i[n]].x*B.y"
  `(,op (ref-of ,name1 ,coord1 ,is1 ,i1) (ref-of ,name2 ,coord2 ,is2 ,i2)))

В итоге вместо этого: (aref some-array 0) у меня есть (aref NIL NIL) который создается в "ref-of".

Я предполагаю, что я теряю символ A от вызова (normalize A). Я просто чувствую, что символ не переживет макрорасширение. Дело в том, что макрорасширение работает в REPL для каждого макроса независимо.

Кто-нибудь может объяснить, где ошибка?


person avp    schedule 26.03.2010    source источник
comment
Можете ли вы быть более описательным, чем не работает? Какой макрос не работает? Представьте, что вы репортер об ошибках.   -  person charlieb    schedule 26.03.2010
comment
Спасибо за отзыв! Я уточнил вопрос.   -  person avp    schedule 26.03.2010
comment
Какой язык (Common Lisp?) и реализацию вы используете?   -  person Chris Johnsen    schedule 26.03.2010
comment
@Svante: я не хочу никаких вызовов функций. Почему я должен?   -  person avp    schedule 26.03.2010
comment
@Chris: на данный момент ccl-1.4 (Clozure CL)   -  person avp    schedule 26.03.2010
comment
@avp: чтобы сделать код понятным (но я думаю, что ответ на этот вопрос уже дан в ответе Пилси). Из вашего комментария см. СТРУКТУРУ ДАННЫХ, у меня сложилось впечатление, что вы могли бы значительно уменьшить сложность своего кода, используя CLOS (см. Гиперспецификацию для defclass, defgeneric и defmethod и главу Переориентация объектов в бесплатной книге Практика Common Lisp). Ваши ref-of и do-op-on, скорее всего, просто исчезнут или будут сведены к простому объявлению в форме defclass.   -  person Svante    schedule 26.03.2010


Ответы (2)


Обратите внимание, что макросы ZERO-P и REF-OF расширяются, когда вы оцениваете, компилируете или загружаете DEFUN для NORMALIZE. Их аргументами являются символы INDEXES и INDEX, и оба они не являются NIL, поэтому все IF в формах REF-OF возьмут первую ветвь и расширятся в формы AREF, где INDICES и INDEX не могут быть привязан к NIL. Короче говоря, вы перепутали время оценки и время макрорасширения, что легко сделать, когда вы только начинаете работать с макросами.

Однако, когда вы вызываете функцию NORMALIZE только с одним аргументом, переменные INDICES и INDEX связываются со значением по умолчанию NIL, поэтому AREF жалуется, что получает недопустимые аргументы.

Лучшее решение, которое я могу вам предложить, это просто превратить ZERO-P и REF-OF в функции вместо макросов. Они будут прекрасно работать как функции, и вам не следует делать макрос, если вы не уверены, что это должен быть макрос. Если вы действительно хотите сохранить их как макросы, задайте значения по умолчанию для INDICES и INDEX, которые имеют смысл, и избавьтесь от необязательных значений в REF-OF и ZERO-P --- я почти уверен, что INDEX по умолчанию равно 0, а INDICES по умолчанию равно #(0 1 2). буду работать.

Добавить РЕДАКТИРОВАТЬ: Желание избежать накладных расходов на вызовы функций почти наверняка не является веской причиной для использования макросов в Лиспе. Во-первых, вам не следует даже беспокоиться о накладных расходах на вызовы функций до тех пор, пока вы не выполните некоторое профилирование и тестирование. Во-вторых, если у вас есть проблема с накладными расходами на вызов функций, вы должны DECLARE использовать рассматриваемые функции INLINE вместо использования макросов для встраивания.

ОТРЕДАКТИРОВАНО еще раз, чтобы добавить: если ваши функции расширяются встроенными, ваш компилятор должен быть в состоянии понять, что он может заменить

(cond ((eq :x :x) 'foo)
      ((eq :x :y) 'bar)
      ((eq :x :z) 'quux)
      (t (error "~A is not one of :X, :Y or :Z" :x))

с участием

'foo
person Pillsy    schedule 26.03.2010
comment
PRAGMA была опечаткой в последний момент. ПРОГ. - person avp; 26.03.2010
comment
Эй, спасибо за ответ. Я просто хочу прояснить одну вещь. Вы сказали: ...не следует делать что-то макросом, если вы не уверены... Так как же я могу быть уверен? Я ввел макросы, чтобы упростить код, а не добавлять какие-либо функции в библиотеку... Достаточно ли этой причины? - person avp; 26.03.2010
comment
Нет, если вы просто хотите упростить свой код, всегда смотрите, можете ли вы сначала разложить вещи на более мелкие функции. Функции проще, чем макросы — их легче тестировать, их можно отследить, и они отображаются в трассировках отладчика. Их также можно использовать напрямую с такими функциями, как MAPCAR, REDUCE или EVERY. - person Pillsy; 26.03.2010
comment
Спасибо! Вы мне очень помогли. - person avp; 26.03.2010

Что такое PRAGMA? Это не стандарт. Возможно, вы имеете в виду PROGN (что даже не обязательно, поскольку DEFUN предоставляет неявный PROGN)?

Зачем все макросы? Есть ли какая-то причина запрещать такую форму, как (reduce (lambda (r c) (* (ref-of A c) r)) (list :x :y :z) :initial-value 1)? Это похоже на случай преждевременной оптимизации.

Ответ Пилси правильный: когда REF-OF расширяется (из-за использования ZERO-P), его INDEXES будет иметь символ INDEXES в качестве значения, а не значение, переданное в NORMALIZE (подобно мудрому INDEX будет I).

person Chris Johnsen    schedule 26.03.2010
comment
Вы правы, я перепутал слова. Я добавил его в последний момент для отладки. Фиксированный. - person avp; 26.03.2010
comment
О макросах: как я могу быть уверен, что в противном случае код будет выполняться во время компиляции? - person avp; 26.03.2010
comment
Да, '(reduce (lambda (c) (ref-of A c)) (list :x :y :z) :initial-value 1)' - хорошая идея, спасибо. - person avp; 26.03.2010