Макрос не работает в функции - PullRequest
4 голосов
/ 26 марта 2010

У меня проблемы со следующим кодом: 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))))))

в функции "normalize" я называю макрос "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)))))

Также в «normalize» я называю макрос «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). Я просто чувствую, что символ не переживает макроэкспансон. Дело в том, что macroexpansoin работает в REPL для каждого макроса независимо.

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

Ответы [ 2 ]

7 голосов
/ 26 марта 2010

Обратите внимание, что макросы ZERO-P и REF-OF раскрываются при оценке, компиляции или загрузке DEFUN для NORMALIZE. Их аргументами являются символы INDEXES и INDEX, и оба они не NIL, поэтому IF s в формах 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
1 голос
/ 26 марта 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).

...