你能在 lisp 中做简单的反引号吗

Can you do simple unquote in lisp

我会尝试重新使用一些 Xlisp-stat 程序,并喜欢从以下内容进行转换

(defproto mls-proto '(id upper lowers logs histories)))

进入

(defclass mls-proto (id upper lowers logs histories))
; or
(defgeneric mls-proto (id upper lowers logs histories))

对于我的下一阶段,尝试将 defmeth 转换为 defmethod。

第一个问题是如何取消引用列表。似乎并不容易,因为那些 ` 和 @ 和 ,@ 似乎还不够。

(defmacro defproto-1 (name &body body)
  `(defclass ,name () ,@body ))

就变成了

(DEFCLASS MLS-PROTO () '(ID UPPER LOWERS LOGS HISTORIES))

有什么提示可以继续吗?

好想变成

 (DEFCLASS MLS-PROTO () (ID UPPER LOWERS LOGS HISTORIES))

根据第一个回答,我采用如下方式解决我的问题:

(defmacro defproto-0 (name (quote slot-names))
  (unless (eq quote 'quote)
    (error "doomed"))
  `(defclass ,name () (,@slot-names)))

(macroexpand-1 '(defproto-0 mls-proto '(id upper lowers logs histories)))

(defproto-0 mls-proto '(id upper lowers logs histories))

但是根据这两个答案,一般的解决方案可能非常困难。我关心的只是迁移 https://www.jasss.org/2/1/3.html 下的一些程序。正如两个答案所指出的,我认为我可能正在处理一个比我想象的更难的问题。尽管建议足够好并且感谢两者,但继续解决更难的 defmeth 问题可能并不那么容易。有人可能认为应该重写它们;这可能更容易。

首先,如有必要,让我们回顾一下撇号 ' 字符是一个 reader 宏,它读取下一个形式 F 并生成一个常规的 Lisp 形式 (quote F).这就是为什么下面有一个quote符号。

所以,在下面的形式中:

(defproto-1 mls-proto '(id upper lowers logs histories)))

defproto-1 被赋予两个未计算的值:

  1. 符号mis-proto

  2. a body,即。所有剩余参数的列表;这里只有一个,所以绑定到 body 的值是一个像这样的列表:

     ((quote (id upper lowers logs histories)))
    

上面我写的列表是在read-time,而不是如果你想让 Lisp 计算它,你需要怎么写它。

区分这一点很重要,因为 parentheses 中的形式通常会被评估,我们需要在它们前面加上引号以将它们评估为文字列表。

body 绑定到这个列表的原因是因为宏不计算它们的参数。在宏调用中,您有 '(id upper lowers logs histories),因此它实际上是 body.

中列表的第一个元素

当你拼接结果代码中的body时,它的所有元素都一个接一个地放在parent形式中,你在[=]里面重新引入(quote ...)形式31=].

如果按如下方式更改宏:

(defmacro defproto-2 (name slots)
   ...)

然后你删除了一层复杂性:你希望调用者只给 defproto-2 一个参数而不是 &body。然后,在下面的调用中:

(defproto-2 mls-proto '(id upper lowers logs histories)))

slots 变量将绑定到:

'(id upper lowers logs histories)

又名

(quote (id upper lowers logs histories))

你仍然不能直接拼接它,因为你的列表前面有一个quote,但是你可以通过调用(second slots)访问列表,跳过第一个元素并使用引号列表(你可以拼接)。

但是,如果你这样做,你就假设来电者总是给你一个引用列表。如果您的代码中存在如下 defproto 实例,则可能会出现问题:

(defproto name some-list-var)

(defproto name (f x y z))

... 其中 some-list-var 是一个符号(绑定到插槽名称列表)和 (f x y z) 一些函数调用。

在那种情况下,您的宏将永远无法工作,因为变量在运行时有值,而不是更早(如果可以在编译时计算表达式,这可能会起作用,在这种情况下您可以 (eval slots) 在你的宏中,但这是一个特例)。

我还假设您正在使用一些您无法更改所有调用的现有代码,否则您可以稍微简化它们并将宏编写为:

;; &body version, body is bound to a list of symbols
(defproto mis-proto id upper lowers logs histories)

;; slots version, where the variable is a literal list
(defproto mis-proto (id upper lowers logs histories))

如果确实存在调用实例,其中槽列表不是引用列表而是需要评估以获得列表的不同形式,那么您的宏需要扩展为可以创建 class 给定一个动态的插槽列表。

在 Common Lisp 中,使用 Meta-Object 协议,那将是 mop:ensure-class。在您的情况下,您可能需要扩展成类似于 (eval (list 'defclass ...)) 的形式,其中 defclass body 包含所需数量的插槽。

你有两个问题,一个小,一个非常大,还有一个可能隐藏在后台的非常大的问题。我假设您的目标语言是 CL。

小的是你的表格是这样的:

(defproto mls-proto '(id upper lowers logs histories)))

其实是这样的:

(defproto mls-proto (quote (id upper lowers logs histories))))

所以,好吧,你可以在宏参数列表中使用解构来解决这个问题'deal':

(defmacro defproto (name (quote slot-names))
  (unless (eq quote 'quote)
    (error "doomed"))
  `(defstruct ,name ,@slot-names))

现在

(defproto mls-proto (quote (id upper lowers logs histories))))
->
(defstruct mls-proto id upper lowers logs histories)

所以说'solves'的问题。除了它没有。因为,据推测,原始来源可以这样做:

(defvar *slots* '(a b c))
...
(defproto mls-proto *slots*)

这将彻底失败。这是个很大的问题。

有两种明智的处理方法:

首先是有一个 class,其中 xlisp-level 插槽是明确管理的。第二种是使用元对象协议以编程方式定义classes。这可能看起来像

(defmacro defproto (name slot-names)
  `(ensure-class ',name :direct-slots (mapcar #'canonicalise-xlisp-slot-spec
                                              ,slot-names)))

我没写的地方canonicalise-xlisp-slot-spec

最后,真正巨大的问题是 XLISP-STAT 中定义的事物的语义不太可能类似于使用 CLOS 定义的事物的语义:进行翻译可能并不简单。