【问题标题】:Argument not passed to lisp macro as intended参数未按预期传递给 lisp 宏
【发布时间】:2019-03-21 16:57:49
【问题描述】:

我觉得我和Basic Lisp Macro error 在同一个球场 但是当我想象代码在展开时应该是什么样子时,我没有看到问题,并且宏扩展没有帮助,因为它只是不想输出任何我可以打印的东西; macroexpand 只是为我运行代码。

(setf my-array (make-array 4 :initial-element 3))
(print my-array)

(setf (aref my-array 2) 5)
(print my-array)

(defmacro set3To5 (arrnum)
    (print (arrayp arrnum))
    (print arrnum)
    (setf (aref arrnum 3) 5)
)
(set3To5 my-array)

运行它会给我输出

argument MY-ARRAY is not an array

但是如果 'arrnum' 被 'my-array' 替换了应该没问题吧?

引用链接的问题

现在在宏展开时,调用宏 ADD-TEST,参数 VAR 得到值 G,一个符号。

当然 my-array 是一个符号,它是我想要操作的符号,为什么会有问题?

【问题讨论】:

  • "macroexpand 没有帮助,因为它只是不想输出任何东西" - 那是因为您的宏不返回任何代码。它只是试图打印一些东西。
  • @melpomene 不应该返回 defmacro 的“body”参数中的代码吗?
  • 不,宏的主体是在调用宏时运行的普通代码,就像函数一样。
  • @melpomene 这确实可以解释为什么它不起作用。谢谢!你帮我弄清楚了。我们的老师指给我们 tutorialspoint.com 和 gigamonkeys 的书,哦,他们确实展示过一次,我错过了。

标签: common-lisp gnu-common-lisp


【解决方案1】:

让我们写下如果一个 lisp 逐行评估你的文件会发生什么:

(setf my-array (make-array 4 :initial-element 3))

此时我们将MY-ARRAY 绑定到#(3 3 3 3)

(print my-array)

打印#(3 3 3 3)

(setf (aref my-array 2) 5)
(print my-array)

修改一个元素并打印#(3 3 5 3)

(defmacro set3To5 (arrnum)
  (print (arrayp arrnum))
  (print arrnum)
  (setf (aref arrnum 3) 5))

现在宏 SET3TO5 已经定义好了。

(set3To5 my-array)

这里的第一步(我们之前没有提到,尽管它正在发生)是宏扩展。编译器知道SET3TO5 是一个宏,因此它以MY-ARRAY(符号)作为参数调用宏函数。让我们看看这个宏里面发生了什么:

(print (arrayp arrnum))

ARRNUM 是符号 MY-ARRAY,所以这会打印 NIL,尽管可能不是您期望的流。

(print arrnum)

这打印MY-ARRAY

(setf (aref arrnum 3) 5)

ARRNUM 不是数组,所以这里有错误。

因此我们未能评估此表达式,因为扩展宏失败。

以下是您可以做的其他一些事情:

(defun set1 (arrnum)
  (print (arrayp arrnum))
  (print arrnum)
  (setf (aref arrnum 3) 5))

(defun set2 (arrnum)
  (list 'setf (list 'aref arrnum 3) 5))

(defmacro set3 (arrnum)
  (list 'setf (list 'aref arrnum 3) 5))

现在评估:

CL-USER> (set1 my-array)
T
#(3 3 5 3)
5
CL-USER> my-array
#(3 3 5 5)
CL-USER> (set2 my-array)
(SETF (AREF #(3 3 5 5) 3) 5)
CL-USER> (set2 'foo)
(SETF (AREF FOO 3) 5)
CL-USER> (setf (aref my-array 3) 1)
1
CL-USER> (set3 my-array)
5
CL-USER> my-array
#(3 3 5 5)

【讨论】:

    【解决方案2】:

    长话短说,代码应该如下所示,为您提供 (3 3 5 5) 的数组。

    (defmacro set3To5 (arrnum)
        `(print (type-of ,arrnum))
        `(setf (aref ,arrnum 3) 5)
    )
    

    至少,GNU Common Lisp 非常关心 ` 和 ' 之间的区别(它们是反引号/反引号和引号/撇号字符)。 What does backtick mean in LISP? 逗号操作符取消对列表中元素的引用,允许您插入参数或局部变量,使用“,@”拼接列表。

    Lisp 宏在其体内执行代码,返回结果应该是可以执行的形式,即宏扩展。

    似乎他们只是决定使用魔法来定义如何将两条反引号行都转换为代码,因为您似乎必须将它们都放在一个列表中并反引号整个列表然后返回该列表,但是这样看起来确实更整洁。

    【讨论】:

    • 不,这是不正确的。宏是从代码到代码的函数,就像任何其他函数一样,只返回隐式 progn 的最后一个值;在这里,中间代码(print 形式)被评估,但其结果被丢弃。如果要返回评估多个表单的代码,请返回 (progn ...)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-26
    • 1970-01-01
    • 2018-05-16
    • 2020-11-14
    • 1970-01-01
    • 2017-07-14
    相关资源
    最近更新 更多