【问题标题】:Why is the @ sign needed in this macro definition?为什么这个宏定义中需要@符号?
【发布时间】:2011-08-05 10:26:10
【问题描述】:

在下面的when宏中:

(defmacro when (condition &rest body)
  `(if ,condition (progn ,@body)))

为什么会有一个“at”@ 符号?

【问题讨论】:

    标签: macros lisp common-lisp quasiquotes


    【解决方案1】:

    在 quasiquoted 部分中插入计算值时,有两个运算符:

    • “逗号”运算符,
    • “逗号拼接”运算符,@

    逗号,在准引用的sendr中插入以下表达式的值,逗号拼接要求后面的表达式是一个列表,并且只能在一个准引用的列表中使用:效果是插入所有元素运算符出现位置的准引号列表中的表达式。

    做个小实验很容易看出区别

    > (let ((x '(1 2 3 4))) `(this is an example ,x of expansion))
    (THIS IS AN EXAMPLE (1 2 3 4) OF EXPANSION)
    
    > (let ((x '(1 2 3 4))) `(this is an example ,@x of expansion))
    (THIS IS AN EXAMPLE 1 2 3 4 OF EXPANSION)
    

    如您所见,,@ 的使用会将列表的元素直接放在展开式内部。没有你得到的列表放在扩展中。

    ,@ 与不生成列表的表达式一起使用将在执行替换时出错:

    * (defun f (x) `(here ,@x we go))
    F
    * (f '(1 2 3))
    (HERE 1 2 3 WE GO)
    * (f '99)
    
    debugger invoked on a TYPE-ERROR in thread
    #<THREAD "main thread" RUNNING {10009F80D3}>:
      The value
        99
      is not of type
        LIST
      when binding SB-IMPL::X
    
    Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.
    
    restarts (invokable by number or by possibly-abbreviated name):
      0: [ABORT] Exit debugger, returning to top level.
    
    (SB-IMPL::APPEND2 99 (WE GO)) [external]
    0] 
    

    在分析准引用部分时,不在列表中使用,@ 反而会出错:

    * (defun g (x) `,@x)
    
    debugger invoked on a SB-INT:SIMPLE-READER-ERROR in thread
    #<THREAD "main thread" RUNNING {10009F80D3}>:
      `,@X is not a well-formed backquote expression
    
        Stream: #<SYNONYM-STREAM :SYMBOL SB-SYS:*STDIN* {10000279E3}>
    
    Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.
    
    restarts (invokable by number or by possibly-abbreviated name):
      0: [ABORT] Exit debugger, returning to top level.
    
    (SB-IMPL::BACKQUOTE-CHARMACRO #<SYNONYM-STREAM :SYMBOL SB-SYS:*STDIN* {10000279E3}> #<unused argument>)
    0] 
    

    【讨论】:

    • 注意不是@x导致扩展,而是,@x
    • 正确...已修复
    【解决方案2】:

    @ 也可以被认为是解构列表并将其附加到列表中,如Practical Common Lisp 中所述。

    `(a ,@(list 1 2) c) 
    

    相当于:

    (append (list 'a) (list 1 2) (list 'c)) 
    

    产生:

    (a 1 2 c)
    

    【讨论】:

      【解决方案3】:

      那个宏定义等价于

      (defmacro when (condition &rest body) 
        (list 'if condition (cons 'progn body)))
      

      但如果没有@,则相当于

      (defmacro when (condition &rest body) 
        (list 'if condition (list 'progn body)))
      

      由于body 是一个列表,这将导致它被评估为一个带括号的函数调用,例如(when t 1 2 3) 会扩展为

      (if t (progn (1 2 3)))
      

      而不是正确的

      (if t (progn 1 2 3))
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-12-05
        • 1970-01-01
        • 2019-11-13
        相关资源
        最近更新 更多