如果在REPL中我这样做:
(dolist (x (1 2 3))
(print x))
然后我得到一个错误,因为在(1 2 3)中数字1不是符号或lambda expr。 如果我这样做:
(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必须像它在传递时那样抱怨(1 2 3) REPL“
显然是错的。但在哪里?
更新:虽然答案有助于澄清对宏的一些误解,但我的问题仍然存在,我将尝试解释原因:
我们已经建立了dolist评估其列表参数(代码块1,2)。好吧,在宏定义中调用它并且传递给它的list参数是定义的宏参数之一(代码块3)似乎并非如此。更多细节: 调用时,宏不会评估其参数。因此,我的测试宏在调用时将保留list参数,并在扩展时将其原样传递给dolist。然后在扩展时,玩家将被执行(在我的测试宏def中没有反引号)。并且它将以(1 2 3)作为参数执行,因为这是测试宏调用传递给它的内容。那么为什么它不会抛出错误,因为dolist试图评估它的列表参数,在这种情况下它的列表参数(1 2 3)是不可评估的。我希望这有点清除我的困惑。
答案 0 :(得分:5)
此表格:
(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“由Peter Seibel或Paul Lisham撰写的”On Lisp“作为介绍 有用的宏。
更新:或许重述一下顺序 阅读,扩展和评估Lisp代码。
首先,REPL接收一系列字符:(
t
e
s
t
(
{ {1}} 1
2
3
)
,它汇集到
令牌:)
(
test
(
1
2
3
)
。
然后,它被翻译成符号树:{)
(test
1
2
))。此步骤中可能涉及所谓的读取器宏。
例如,3
被翻译为('x
quote
)。
然后,从外面开始,操作员位置中的每个符号(即,
检查表格中的第一个位置。如果它命名一个宏,那么
使用代码调用相应的宏函数(即,
符号子树)作为参数的形式的其余部分。该
宏函数应该返回一个新的形式,即代码,其中
替换宏表单。在您的情况下,宏x
获取代码
(test
1
2
)作为参数,打印其中包含的每个符号
(注意,这甚至在编译时间之前),并返回3
,
扔掉它的论点(编译器甚至看不到你的小东西
列表)。然后再次检查返回的代码是否可能
macroexpansions。
最后,扩展的代码不包含任何宏调用
评估,即编译和执行。 nil
碰巧
是一个自我评价的象征;它评估为Nil
。
这只是一个粗略的草图,但我希望它能解决一些问题。
答案 1 :(得分:4)
您的宏test
不会返回任何代码。是的,不评估宏参数。如果要查看相同的错误,则必须将宏定义为:
(defmacro test(lst) `(dolist(x,lst) (打印x)))
答案 2 :(得分:4)
通常,如果您对宏扩展有疑问,请参考MACROEXPAND-1
是第一步。
* (macroexpand-1 '(test (1 2 3)))
1
2
3
NIL
T
IE,正在发生的事情是实际的扩展是那个打印序列。
Nil是DOLIST
返回的内容,是扩展代码。
答案 3 :(得分:1)
宏使得他们的论点通过了无价值。他们可能会选择评估它们。 dolist
为其列表参数执行此操作。它适用于您的宏lst
中传递给test
的不带引号的列表:
(defmacro test (lst)
(dolist (x lst)
(print x)))
那是因为在宏扩展时,dolist
将lst
视为其参数。因此,在对其进行评估时,它会获得列表(1 2 3)
。
答案 4 :(得分:0)
lst是一个变量,当扩展宏测试时,它也意味着eval dolist结构。第一步是评估表单lst,将得到lisp对象(1 2 3)。
如下例:
(defmacro测试(一) (+ a 2))
(测试2) - > 4;表示调用add函数,第一个变量a绑定值为2。