宏在功能中不起作用

时间:2010-03-26 12:22:48

标签: macros lisp common-lisp

我遇到以下代码问题: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)我在“ref-of”中创建了(aref NIL NIL)

我想我从通话(normalize A)中丢失了符号A.我只是觉得这个符号不能在macroexpanson中存活下来。问题是,macroexpansoin独立地在每个宏的REPL中工作。

任何人都可以解释错误在哪里?

2 个答案:

答案 0 :(得分:7)

请注意,在评估,编译或加载ZERO-P REF-OF时,会扩展宏DEFUNNORMALIZE。他们的论点是符号 INDEXESINDEX,而且这两个都不是NIL,所以IFREF-OF中表单将全部采用第一个分支并扩展为AREF表单,其中INDICESINDEX无法绑定到NIL。简而言之,您已经将评估时间和宏扩展时间混淆了,当您刚开始使用宏时,这是一件容易的事情。

但是,当您仅使用一个参数调用函数NORMALIZE时,变量INDICESINDEX将绑定到默认值NIL,因此{{1抱怨它得到了无效的参数。

我能为您提供的最佳解决方案是将AREFZERO-P转换为函数而不是宏。它们作为函数可以正常工作,除非你确定它需要是一个宏,否则你不应该创建宏。如果您确实希望将它们保留为宏,请将REF-OFINDICES的默认值设为有意义,并删除INDEXREF-OF中的可选值 - - 我非常确定ZERO-P默认为0,INDEX默认为INDICES

编辑添加:想要避免函数调用开销几乎肯定不是在Lisp中使用宏的好理由。首先,在完成一些分析和测试之后,您甚至不应该担心函数调用开销。第二,如果你确实遇到函数调用开销问题,你应该#(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))

答案 1 :(得分:1)

什么是PRAGMA?这不标准。也许你的意思是PROGN(由于DEFUN提供了隐式PROGN,因此甚至没有必要?)

为什么所有的宏?是否有理由拒绝(reduce (lambda (r c) (* (ref-of A c) r)) (list :x :y :z) :initial-value 1)之类的表格?这似乎是premature optimization的情况。

Pillsy的答案是正确的:当REF-OF展开时(使用ZERO-P),其INDEXES将使用符号INDEXES作为其值,而不是传入NORMALIZE的值(与智慧INDEX一样,将为I)。