Common Lisp:将符号传递给宏

Squ*_*row 3 macros arguments symbols metaprogramming common-lisp

此宏的目的是创建一个宏,该宏为访问关联列表的某个键提供名称.

(defmacro generate-accessor (key-symbol prefix)
  (let ((mac-name 
          (intern (string-upcase (concatenate 'string
                                              prefix "-"
                                              (string key-symbol))))))
    `(defmacro ,mac-name (alis) `(assoc ,',key-symbol ,alis))))
Run Code Online (Sandbox Code Playgroud)

所以,当我尝试它 -

CL-USER> (generate-accessor 'a "alist")
; ERROR> 'A cannot be coerced to a string.
Run Code Online (Sandbox Code Playgroud)

但是...

CL-USER> (string 'a)
; RESULT> "A"
Run Code Online (Sandbox Code Playgroud)

所以我再次尝试使用SYMBOL-NAME将符号强制转换为字符串

(defmacro generate-accessor (key-symbol prefix)
  (let ((mac-name 
          (intern (string-upcase (concatenate 'string
                                              prefix "-"
                                              (symbol-name key-symbol))))))
    `(defmacro ,mac-name (alis) `(assoc ,',key-symbol ,alis))))
Run Code Online (Sandbox Code Playgroud)

这次我试试 -

CL-USER> (generate-accessor 'a "alist")
; ERROR> The value 'A is not of type SYMBOL.
Run Code Online (Sandbox Code Playgroud)

但是...

CL-USER> (symbol-name 'a)
; RESULT>"A"
CL-USER> (symbolp 'a)
; RESULT>T
Run Code Online (Sandbox Code Playgroud)

每当我'a在我的宏之外使用时,它会像我期望的那样自动作为符号实现.然而不知何故,当我传递'a给我的宏时,它作为一个引用的块来到.我不明白为什么它没有被评估,特别是在反引号开始之前.我知道我不理解Lisp的一些基本内容,但我不知道如何立即看到它.

Jos*_*lor 11

'a是简写(quote a),这是您传递给宏的列表.宏参数不会被评估,但会按原样传递.当作为参数传递给一个功能(即,不向宏)中使用(quote a)的评价首先,和结果评价 (quote a)是符号a.例如,考虑之间的区别

(list 'a)           ===
(list (quote a)) 
; => (a)
Run Code Online (Sandbox Code Playgroud)

和

'('a)               ===
'((quote a))        ===   
(quote ((quote a)))
; => ((quote a)) ;; which may also be printed ('a)
Run Code Online (Sandbox Code Playgroud)

使用符号参数到宏的示例

根据评论中的请求,这里是一个类似defstruct的宏,它创建了一些包含结构名称的函数.

(defmacro my-defstruct (name slot)
  "A very poor implementation of defstruct for structures
that have exactly one slot"
  (let ((struct-name (string name))
        (slot-name (string slot)))
    `(progn
       (defun ,(intern (concatenate 'string (string '#:make-) struct-name)) (value)
         (list value))
       (defun ,(intern (concatenate 'string (string struct-name) "-" slot-name)) (structure)
         (car structure)))))
Run Code Online (Sandbox Code Playgroud)

这是例如(my-defstruct foo bar)扩展到:

CL-USER> (pprint (macroexpand '(my-defstruct foo bar)))

(PROGN
 (DEFUN MAKE-FOO (VALUE) (LIST VALUE))
 (DEFUN FOO-BAR (STRUCTURE) (CAR STRUCTURE)))
Run Code Online (Sandbox Code Playgroud)

使用示例:

CL-USER> (my-defstruct foo bar)
FOO-BAR
CL-USER> (make-foo 34)
(34)
CL-USER> (foo-bar (make-foo 34))
34
Run Code Online (Sandbox Code Playgroud)


Rai*_*wig 8

每当我使用'我的宏之外,它会像我期望的那样自动作为符号实现.

为什么.我希望它是一个引用的形式,在评估时,我得到引用的对象.

请记住:宏转换源代码,而不是评估对象.

由于您的代码没有对其进行评估并且您SYMBOL-NAME使用引用的表单进行调用,因此我希望这是一个错误.

您可以像其他代码一样调试宏.打印出宏作为参数的内容,或者只是使用调试器来查看回溯并查看各种变量值.

宏CHECK-TYPE检查某个地方的类型.

CL-USER 15 > (defmacro generate-accessor (key-symbol prefix)
               (check-type key-symbol symbol)
               (check-type prefix string)
               (let ((mac-name 
                      (intern (string-upcase (concatenate 'string
                                                          prefix "-"
                                                          (string key-symbol))))))
                 `(defmacro ,mac-name (alis) `(assoc ,',key-symbol ,alis))))
GENERATE-ACCESSOR

CL-USER 16 > (generate-accessor 'a "alist")

Error: The value (QUOTE A) of KEY-SYMBOL is not of type SYMBOL.
  1 (continue) Supply a new value of KEY-SYMBOL.
  2 (abort) Return to level 0.
  3 Return to top loop level 0.
Run Code Online (Sandbox Code Playgroud)

所以CHECK-TYPE抱怨它不是一个符号,而是两个元素的列表.这是预期的,因为宏适用于源代码,而不是评估对象.

顺便说一句,将访问器编写为宏已经是错误的.Common Lisp具有函数 - 使用它们.