Macros lisp中的宏行为问题

Macros lisp中的宏行为问题,macros,lisp,common-lisp,Macros,Lisp,Common Lisp,如果在REPL中,我执行以下操作: (dolist (x (1 2 3)) (print x)) 然后我得到一个错误,因为在(1233)中,数字1不是符号或lambda表达式。 如果我这样做: (dolist (x (list 1 2 3)) (print x)) 那就行了 我的问题是,为什么以下方法有效: REPL> (defmacro test (lst) (dolist (x lst) (print x))) =&g

如果在REPL中,我执行以下操作:

(dolist (x (1 2 3))
  (print x))
然后我得到一个错误,因为在(1233)中,数字1不是符号或lambda表达式。 如果我这样做:

(dolist (x (list 1 2 3))
      (print x))
那就行了

我的问题是,为什么以下方法有效:

REPL> (defmacro test (lst)
           (dolist (x lst)
             (print x)))
=> TEST
REPL> (test (1 2 3))
1
2
3
=>NIL
为什么当dolist在宏定义中时接受(1 2 3),而直接在repl中时不接受? 假设:

由于TEST是一个宏,它不计算其参数,因此(1 2 3)按原样传递给dolist宏。因此,dolist必须像在REPL中传递(1 2 3)时一样发出抱怨

这显然是错误的。但是在哪里呢

更新:尽管这些答案有助于澄清对宏的一些误解,但我的问题仍然存在,我将尝试解释原因:

我们已经确定dolist计算它的list参数(代码块1,2)。好吧,在宏定义中调用它,并且传递给它的列表参数是定义的宏参数之一(代码块3)时,情况似乎并非如此。更多详情:
调用宏时,不会计算其参数。因此,调用我的test宏时,它将保留list参数,并在扩展时将其原样传递给dolist。然后在展开时将执行dolist(在我的测试宏def中没有反引号)。它将以(1 2 3)作为参数执行,因为这是测试宏调用传递给它的内容。既然dolist试图计算它的list参数,那么为什么它不抛出一个错误呢?在本例中,它的list参数(1、2、3)是不可计算的。我希望这能消除我的困惑。

您的宏
测试
不会返回任何代码。是的,不计算宏参数。如果要看到相同的错误,必须将宏定义为:

(DEF宏观测试(lst) `(多利斯特(x,lst) (打印x))

此表格:

(defmacro test (lst)
  (dolist (x lst)
    (print x)))
定义一个宏,它是一个“代码转换函数”,用于 在宏展开时使用此宏应用于窗体。所以 定义此宏后,计算此表达式时:

(test (1 2 3))
它首先读取到此列表:

(test (1 2 3))
然后,由于Lisp在操作员位置读取
test
,因此 宏通过传递参数展开,该参数是文本列表
(1
2.3)
,用于上述定义的宏扩展功能。这意味着 在宏展开时对以下各项进行求值:

(dolist (x '(1 2 3))
  (print x))
因此,在宏展开时,将打印这三个值。最后 该表单的返回值作为要编译的代码返回 然后被处决
Dolist
在此处返回
nil
,因此这是返回的代码:

nil
Nil
的计算结果为
Nil
,返回该值

一般来说,这样的宏不是很有用。见“实用通用”一节 彼得·塞贝尔的《口齿不清》或保罗·格雷厄姆的《关于口齿不清》作为介绍 到有用的宏


更新:概括一下 读取、扩展和评估Lisp代码

首先,REPL接收字符流:
t
e
s
t

1
2
3
,将其组装成 令牌:
测试
1
2
3

然后,将其转换为符号树:(
test
1
2
3
))。此步骤可能涉及所谓的读卡器宏。 例如,
'x
被翻译为(
quote
x

然后,从外到内,每个符号位于操作员位置(即 检查表格中的第一个位置)。如果它命名宏,则 使用代码调用相应的宏函数(即 符号子树)作为参数的形式的其余部分。这个 宏函数应该返回一个新的表单,即代码,它 替换宏窗体。在您的例子中,宏
test
获取代码 (
1
2
3
)作为参数,打印其中包含的每个符号 (请注意,这甚至在编译时之前),并返回
nil
, 扔掉它的参数(编译器甚至从来没有看到你的小 列表)。然后再次检查返回的代码,以查找可能的错误 宏观扩张

最后,是不包含任何宏调用的扩展代码 再计算,即编译和执行<代码>无
发生在 成为自我评价的象征;它的计算结果为
nil


这只是一个粗略的草图,但我希望它能澄清一些问题。

通常,当您对宏扩展有疑问时,参考
MACROEXPAND-1
是一个很好的第一步

* (macroexpand-1 '(test (1 2 3)))

1 
2 
3 
NIL
T
也就是说,实际的扩展是打印序列


Nil是由
DOLIST
返回的值,是扩展代码。

宏将其未赋值的参数传递给用户。他们可以选择对其进行评估
dolist
为其列表参数执行此操作。它与宏
test
中为
lst
传入的无引号列表一起工作:

(defmacro test (lst)
  (dolist (x lst)
    (print x)))

这是因为在宏展开时,
dolist
lst
视为其参数。因此,当它对它求值时,它会得到列表
(1 2 3)

lst是一个变量,当展开宏测试时,它也意味着求值列表结构。第一步是计算表单lst,它将得到lisp对象(1,2,3)

例如:

(宏观测试(a) (+a 2))


(测试2)->4;mean invoke add函数,第一个变量a绑定一个值2。

我不明白的是,为什么我传递给宏调用的形式(1 2 3)在宏扩展时变成了“(1 2 3),而不是停留在(1 2 3)@Paralife:它仍然是
(1 2 3)
,它只是没有得到计算。我用I模拟了实际的评估