【问题标题】:Macro doesn't work in the function宏在函数中不起作用
【发布时间】:2010-03-26 12:22:47
【问题描述】:

以下代码有问题: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)))))

此外,在“规范化”中,我将宏称为“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”中创建的。

我想我在调用 (normalize A) 时丢失了符号 A。我只是觉得这个符号在宏扩展中无法生存。问题是,macroexpansoin 在 REPL 中独立地为每个宏工作。

谁能解释错误在哪里?

【问题讨论】:

  • 你能比“不工作”更具描述性吗?哪个宏不起作用?想象一下,您是一名错误报告者。
  • 感谢您的反馈!我澄清了这个问题。
  • 你使用什么语言(Common Lisp?)和实现?
  • @Svante:我不想要函数调用。我为什么要这样做?
  • @avp:使代码易于理解(但我认为这已经在 Pillsy 的回答中得到了回答)。从您的评论“查看数据结构”中,我的印象是您可以通过使用 CLOS 大大降低代码复杂性(请参阅 defclassdefgenericdefmethod 的超规范,以及“对象重新定向”一章在免费书籍“实用通用 Lisp”中)。您的 ref-ofdo-op-on 可能会消失,或者简化为 defclass 表单中的简单声明。

标签: macros lisp common-lisp


【解决方案1】:

请注意,当您为NORMALIZE 评估、编译或加载DEFUN 时,宏ZERO-PREF-OF 会展开。他们的论点是符号 INDEXESINDEX,而这两个都不是NIL,所以REF-OF 表单中的IFs 都将采用第一个分支并展开进入AREF 表单,其中INDICESINDEX 不能绑定到NIL。简而言之,您将评估时间和宏扩展时间混淆了,当您刚开始使用宏时,这很容易做到。

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

我可以为您提供的最佳解决方案是将ZERO-PREF-OF 变成函数而不是宏。它们可以作为函数正常工作,除非您确定它需要成为宏,否则不应将其设为宏。如果您确实想将它们保留为宏,请为INDICESINDEX 提供有意义的默认值,并去掉REF-OFZERO-P 中的可选值---我很确定有@ 987654345@ 默认为 0 和 INDICES 默认为 #(0 1 2) 将起作用。

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

【讨论】:

  • PRAGMA 在最后一刻是印刷错误。项目。
  • 您好,谢谢您的回答。我只想澄清一件事。你说过:“……除非你确定……不应该把某事做成宏……”。那我怎么能确定呢?我引入宏是为了简化代码,而不是在库中添加任何函数……这个理由够好吗?
  • 不,如果你只是想简化你的代码,总是先看看你能不能把事情分解成更小的函数。函数比宏更简单——它们更容易测试,它们可以被跟踪,并且它们显示在调试器回溯中。它们也可以直接与MAPCARREDUCEEVERY 等函数一起使用。
  • 谢谢!你帮了我很大的忙。
【解决方案2】:

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

【讨论】:

  • 你说得对,我把这个词弄混了。我在最后一刻添加了它以进行调试。固定。
  • 关于宏:否则我如何确定代码会在编译时执行?
  • 是的,'(reduce (lambda (c) (ref-of A c)) (list :x :y :z) :initial-value 1)' 是个好主意,谢谢。跨度>
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多