开发者

Macro doesn't work in the function

I have problems with following code: http://lisper.ru/apps/format/96

The problem is in "normalize" function, which does not work.

It fails on the fifth line: (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))))))

in function "normalize" I call the macro "zero-p", which in turn calls macro "ref-of", which is the last in the chain.开发者_StackOverflow

(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-*)))

Here is ref-of:

(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)))))

Also, in "normalize" I call the macro "do-op-on", which calls "ref-of" as well.

(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)))

As a result, instead of having this: (aref some-array 0) I have (aref NIL NIL) which is created in "ref-of".

I suppose that I lose the symbol A from the call (normalize A). I just feel that the symbol does not survive the macroexpanson. The thing is, macroexpansoin works in REPL for each macro independently.

Can anyone explain where is the mistake?


Note that the macros ZERO-P and REF-OF are expanded when you evaluate, compile or load the DEFUN for NORMALIZE. Their arguments are the symbols INDEXES and INDEX, and both of those are not NIL, so the IFs in the REF-OF forms will all take the first branch and expand into AREF forms where INDICES and INDEX can't be bound to NIL. In short, you've gotten evaluation time and macroexpansion time confused, which is an easy thing to do when you are just starting with macros.

However, when you call the function NORMALIZE with only one argument, the variables INDICES and INDEX are bound to a default value of NIL, so AREF complains that it's getting invalid arguments.

The best solution I can offer you is to just make ZERO-P and REF-OF into functions instead of macros. They'll work just fine as functions, and you shouldn't make something a macro unless you're sure it needs to be a macro. If you really do want to keep them as macros, give default values to INDICES and INDEX that make sense and get rid of the optional values in REF-OF and ZERO-P---I'm pretty sure having INDEX default to 0 and INDICES default to #(0 1 2) will work.

EDIT to add: Wanting to avoid function call overhead is almost certainly not a good reason to use macros in Lisp. In the first place, you shouldn't even worry about function call overhead until after you've done some profiling and testing. In the second place, if you do have a problem with function call overhead, you should DECLARE the functions in question INLINE instead of using macros to do the inlining.

EDITed again to add: If your functions are being expanded inline, your compiler should be able to figure out that it can replace

(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))

with

'foo


What is PRAGMA? It is not standard. Maybe you mean PROGN (which is not even necessary since DEFUN supplies an implicit PROGN)?

Why all the macros? Is there some reason to disallow a form like (reduce (lambda (r c) (* (ref-of A c) r)) (list :x :y :z) :initial-value 1)? This seems like a case of premature optimization.

Pillsy's answer is right on: when REF-OF is expanded (from the usage of ZERO-P), its INDEXES will have the symbol INDEXES as its value, not the value passed into NORMALIZE (like wise INDEX will be I).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜