你能在 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
被赋予两个未计算的值:
符号mis-proto
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 定义的事物的语义:进行翻译可能并不简单。
我会尝试重新使用一些 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
被赋予两个未计算的值:
符号
mis-proto
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 定义的事物的语义:进行翻译可能并不简单。