Macros lisp中的宏行为问题
如果在REPL中,我执行以下操作: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
(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模拟了实际的评估