lisp中的宏行为问题

时间:2011-02-23 17:07:43

标签: macros lisp common-lisp

如果在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)是不可评估的。我希望这有点清除我的困惑。

5 个答案:

答案 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)))

那是因为在宏扩展时,dolistlst视为其参数。因此,在对其进行评估时,它会获得列表(1 2 3)

答案 4 :(得分:0)

lst是一个变量,当扩展宏测试时,它也意味着eval dolist结构。第一步是评估表单lst,将得到lisp对象(1 2 3)。

如下例:

(defmacro测试(一)    (+ a 2))

(测试2) - > 4;表示调用add函数,第一个变量a绑定值为2。