gpt4 book ai didi

macros - 定义名称基于宏参数的函数的宏

转载 作者:行者123 更新时间:2023-12-04 14:57:26 25 4
gpt4 key购买 nike

*注意:尽管我经常光顾 StackOverflow 很长时间,但这是我自己发布的第一个问题。如果有点冗长,请见谅。 build 性的批评表示赞赏。

当我在 Common Lisp 中使用 defstruct 定义一个结构体时,会自动生成一个谓词函数来测试它的参数是否是由 defstruct 定义的类型。例如:

(defstruct book
title
author)

(let ((huck-finn (make-book :title "The Adventures of Huckleberry Finn" :author "Mark Twain")))
(book-p huck-finn))
=> True

然而,当使用 defclass 定义一个类时,这些函数似乎不是默认生成的(有没有办法指定这个?),所以我试图自己添加这个功能,因为我想要 a) 为这个语法在结构和类之间保持一致,b) 使用缩写 (typep obj 'classname) ,我需要经常写并且在视觉上很嘈杂,
c) 作为编程练习,因为我对 Lisp 还比较陌生。

我可以编写一个宏来定义给定类名的谓词函数:
(defclass book ()
((title :initarg :title
:accessor title)
(author :initarg :author
:accessor author)))

;This...
(defmacro gen-predicate (classname)
...)

;...should expand to this...
(defun book-p (obj)
(typep obj 'book))

;...when called like this:
(gen-predicate 'book)

我需要传递给 defun 的名称必须采用 'classname-p 形式.这就是我遇到困难的地方。要创建这样的符号,我可以使用 Paul Graham 的 On Lisp (p. 58) 中的“symb”函数。当它在 REPL 上运行时:
(symb 'book '-p)
=> BOOK-P

到目前为止,我的 gen-predicate 宏如下所示:
(defmacro gen-predicate (classname)
`(defun ,(symb classname '-p) (obj)
(typep obj ,classname)))

(macroexpand `(gen-predicate 'book))
=>
(PROGN
(EVAL-WHEN (:COMPILE-TOPLEVEL) (SB-C:%COMPILER-DEFUN '|'BOOK-P| 'NIL T))
(SB-IMPL::%DEFUN '|'BOOK-P|
(SB-INT:NAMED-LAMBDA |'BOOK-P|
(OBJ)
(BLOCK |'BOOK-P| (TYPEP OBJ 'BOOK)))
NIL 'NIL (SB-C:SOURCE-LOCATION)))
T

似乎是由 (symb 'book '-p) 创建的符号实际上被认为是 |'BOOK-P|通过实现(SBCL),而不是 BOOK-P .果然,这现在有效:
(let ((huck-finn (make-instance 'book)))
(|'BOOK-P| huck-finn))
=> True

为什么 symb 创建的符号是 |'BOOK-P| ?在 On Lisp(与上面相同的页面)中,Graham 说:“任何字符串都可以是符号的打印名称,甚至是包含小写字母或括号等宏字符的字符串。当一个符号的名称包含这些奇怪的东西时,它会在垂直方向上打印酒吧。”在这种情况下不存在这样的怪事,是吗?我认为符号的“打印名称”是打印符号时实际显示在标准输出上的内容是否正确,并且在这种奇怪的情况下,与符号本身的形式不同?

能够编写函数定义宏,如 gen-predicate - 其定义的函数是根据传递给宏的参数命名的 - 在我看来,这就像 Lisp 黑客可能多年来一直在做的事情。用户 Kaz 在这里说 ( Merging symbols in common lisp ) 通常可以避免符号的“混搭”,但这会破坏这个宏的目的。

最后,假设我能得到 gen-predicate以我想要的方式工作,确保为每个定义的新类调用它的最佳方法是什么?与 initialize-instance 的方式大致相同可以自定义以在类实例化时执行某些操作,是否有由 defclass 调用的通用函数可以在类定义时执行操作?

谢谢你。

最佳答案

这是一个常见的问题:传递给宏的是什么?

比较这样的调用:

(symb 'book '-p)


(symb ''book '-p)

你的宏形式是这样的:
(gen-predicate 'book)
GEN-PREDICATE是一个宏。 classname是这个宏的参数。

现在 classname 的值是多少?在代码扩展期间在宏内部?是吗 book'book ?

其实是后者,因为你写了 (gen-predicate 'book) .请记住:宏查看源代码,参数源被传递给宏函数 - 而不是值。参数是 'book .这样就通过了。 (QUOTE BOOK)是一样的,只是打印不同。所以它是一个两元素列表。第一个元素是符号 QUOTE第二个元素是符号 BOOK .

因此,宏现在调用函数 SYMB使用参数值 (QUOTE BOOK)或者,更短的, 'BOOK .

如果要生成不带引号的谓词,需要写:
(gen-predicate book)

或者,您也可以更改宏:
(symb classname '-p)

将是:
(symbol (if (and (consp classname)
(eq (first classname) 'quote))
(second classname)
classname))

比较

我们写
(defun foo () 'bar)

并不是
(defun 'foo () 'bar)    ; note the quoted FOO
DEFUN是一个宏,第一个参数是函数名。然后是类似的问题...

问题的第二部分

我真的不知道有什么好的答案。我不记得在类定义之后运行代码(例如定义函数)的任何简单方法。
  • 也许使用 MOP,但这很丑陋。
  • 编写自定义宏 DEFINE-CLASS做你想做的事:扩展成 DEFCLASSDEFUN .
  • 遍历包中的所有符号,找到类并定义相应的谓词
  • 关于macros - 定义名称基于宏参数的函数的宏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30415512/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com