lisp中的宏行为问题

Par*_*ife 3 lisp macros common-lisp

如果在REPL中我这样做:

(dolist (x (1 2 3))
  (print x))
Run Code Online (Sandbox Code Playgroud)

然后我得到一个错误,因为在(1 2 3)中数字1不是符号或lambda expr.如果我做:

(dolist (x (list 1 2 3))
      (print x))
Run Code Online (Sandbox Code Playgroud)

然后它工作正常.

我的问题是为什么以下工作:

REPL> (defmacro test (lst)
           (dolist (x lst)
             (print x)))
=> TEST
REPL> (test (1 2 3))
1
2
3
=>NIL
Run Code Online (Sandbox Code Playgroud)

为什么dolist在宏定义中接受(1 2 3)而在直接在repl中时不接受?假设:

"由于TEST是一个宏,它不会评估它的参数,因此(1 2 3)会按原样传递给dolist宏.因此,玩家必须像在REPL中传递(1 2 3)时那样抱怨"

显然是错的.但是哪里?

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

我们已经建立了dolist评估其列表参数(代码块1,2).好吧,在宏定义中调用它并且传递给它的list参数是定义的宏参数之一(代码块3)似乎并非如此.更多细节:调用时,宏不会评估其参数.因此,我的测试宏在调用时将保留list参数,并在扩展时将其原样传递给dolist.然后在扩展时,玩家将被执行(在我的测试宏def中没有反引号).并且它将以(1 2 3)作为参数执行,因为这是测试宏调用传递给它的内容.那么为什么它不会抛出错误,因为dolist试图评估它的列表参数,在这种情况下它的列表参数(1 2 3)是不可评估的.我希望这有点清除我的困惑.

Sva*_*nte 5

这种形式:

(defmacro test (lst)
  (dolist (x lst)
    (print x)))
Run Code Online (Sandbox Code Playgroud)

定义一个,它是一个"代码转换函数",它在宏扩展时使用该宏应用于表单.因此,在定义此宏之后,在计算此表达式时:

(test (1 2 3))
Run Code Online (Sandbox Code Playgroud)

它首先被读到这个列表:

(test (1 2 3))
Run Code Online (Sandbox Code Playgroud)

然后,由于Lisp test在操作员位置读取,因此通过将参数(即文字列表(1 2 3))传递给上面定义的宏扩展函数来进行宏扩展.这意味着在宏扩展时评估以下内容:

(dolist (x '(1 2 3))
  (print x))
Run Code Online (Sandbox Code Playgroud)

因此,在宏观扩展时,会打印出三个值.最后,返回该表单的返回值作为要编译和执行的代码. Dolist返回nil这里,所以这是返回的代码:

nil
Run Code Online (Sandbox Code Playgroud)

Nil评估为nil,返回.

通常,这样的宏不是很有用.请参阅Peter Seibel的"Practical Common Lisp"或Paul Graham的"On Lisp",了解有用的宏.


更新:概括阅读,扩展和评估Lisp代码的顺序可能很有用.

首先,REPL接收一个字符流:( t e s t ( 1 2 3 ) )它组装成令牌:( test ( 1 2 3 ) ).

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

然后,从外部进入,检查操作员位置中的每个符号(即,表格中的第一个位置).如果它命名一个宏,则使用代码(即符号子树)调用相应的宏函数,该代码是表单的其余部分作为参数.宏函数应该返回一个新的形式,即代码,它取代了宏形式.在您的情况下,宏test获取code(1 2 3)作为参数,打印其中包含的每个符号(请注意,这甚至在编译时间之前),并返回nil,抛出其参数(编译器甚至从未看到您的小列表).然后再次检查返回的代码以查找可能的宏扩展.

最后,评估,即编译和执行不再包含任何宏调用的扩展代码. Nil恰好是一个自我评价的象征; 它评估为nil.

这只是一个粗略的草图,但我希望它清除一些东西.

  • @Paralife:因为DOLIST宏扩展到的代码恰好将对象`(1 2 3)`置于将被评估的位置.你可以用`(macroexpand-1'(dolist(x(1 2 3))(print x)))`的形式看到它. (2认同)