如何在lisp中定义具有任意参数数的结构?

如何在lisp中定义具有任意参数数的结构?,lisp,structure,common-lisp,Lisp,Structure,Common Lisp,我试图定义一个结构,其中包含一些我知道需要的属性,以及基本结构不需要的任意数量的其他属性 (defstruct (node (:type list)) label [other args here]) 我知道在函数中,您可以执行以下操作: (defun foo (arg1 &rest args) ...) 对于defstruct,是否存在某种类型的&rest等价物 我只是在学习口齿不清,所以我觉得我错过了一些东西。如果没有一个&rest等价物,我该如何处理这个问题呢?提前谢谢 你到底

我试图定义一个结构,其中包含一些我知道需要的属性,以及基本结构不需要的任意数量的其他属性

(defstruct (node (:type list)) label [other args here])
我知道在函数中,您可以执行以下操作:

(defun foo (arg1 &rest args) ...)
对于
defstruct
,是否存在某种类型的
&rest
等价物


我只是在学习口齿不清,所以我觉得我错过了一些东西。如果没有一个
&rest
等价物,我该如何处理这个问题呢?提前谢谢

你到底在找什么还不清楚。结构的默认情况是一种记录类型,具有固定数量的插槽,每个插槽都有一个名称,可以通过
defstruct
宏生成的函数进行访问。例如,一旦你完成了

(defstruct node 
  label)
您可以使用
节点标签
访问
节点的标签,并获得快速查找时间(因为它通常只是内存块的索引)。现在,您可以选择使用列表作为结构的实现,在这种情况下,
node label
只是
car
first
的别名

(defstruct (node (:type list))
  label)

CL-USER> (make-node :label 'some-label)
(SOME-LABEL)
CL-USER> (node-label (make-node :label 'some-label))
SOME-LABEL
CL-USER> (first (make-node :label 'some-label))
SOME-LABEL
CL-USER> (car (make-node :label 'some-label))
如果要查找任意基于列表的键值对,可能需要一个,Common Lisp包含一些方便的函数

如果希望结构也包含属性列表,可以添加一个特殊的构造函数来填充该列表。比如说,

(defstruct (node (:type list)
                 (:constructor make-node (label &rest plist)))
  label
  plist)

CL-USER> (make-node 'some-label :one 1 :two 2)
(SOME-LABEL (:ONE 1 :TWO 2))
CL-USER> (node-plist (make-node 'some-label :one 1 :two 2))
(:ONE 1 :TWO 2)

在常见的Lisp中,结构被认为是刚性的低级记录。它们没有奇特的动态特性

对于结构,您可以定义一个从另一个继承的新结构类型。只有一个继承可用

要处理动态扩展性,典型的方法是向结构添加属性列表槽。见约书亚的回答

然后是公共Lisp对象系统,它提供了多重继承,您可以在运行时更改类。因此,您可以向一个类添加一个插槽,该类的实例会自动更新。您还可以更改对象的类别,插槽可能会被添加或删除。不过,通常一个类的所有实例都将具有相同的插槽集。同样,可以添加一个带有属性列表的插槽,并将其用于扩展性

CommonLisp还有其他对象系统,可以轻松地在每个实例基础上添加插槽。但是仅仅为了这个目的使用它们通常太多了,因为它们的功能更强大

使用CLOS和元对象协议,可以尝试隐藏它。这里我使用的是LispWorks:

我们为属性定义了一个mixin类:

(defclass property-mixin ()
  ((plist :initform nil))
  #+lispworks
  (:optimize-slot-access nil))
(defmethod set-property ((object property-mixin) key value)
  (setf (getf (slot-value object 'plist) key) value))

(defmethod get-property ((object property-mixin) key)
  (getf (slot-value object 'plist) key))
设置和读取属性:

(defclass property-mixin ()
  ((plist :initform nil))
  #+lispworks
  (:optimize-slot-access nil))
(defmethod set-property ((object property-mixin) key value)
  (setf (getf (slot-value object 'plist) key) value))

(defmethod get-property ((object property-mixin) key)
  (getf (slot-value object 'plist) key))
现在我们编写方法,使
SLOT-VALUE
接受我们的属性名:

(defmethod (setf clos:slot-value-using-class)
       (value (class standard-class) (object property-mixin) slot-name)
  (declare (ignorable class)) 
  (if (slot-exists-p object slot-name)
      (call-next-method)
    (progn
      (set-property object slot-name value)
      value)))

(defmethod clos:slot-value-using-class ((class standard-class)
                                        (object property-mixin)
                                        slot-name)
  (declare (ignorable class))
  (if (slot-exists-p object slot-name)
      (call-next-method)
    (get-property object slot-name)))
例如。我们定义了一个具有两个插槽的automobile类:

(defclass automobile (property-mixin)
  ((company :initarg :company)
   (motor :initarg :motor))
  #+lispworks
  (:optimize-slot-access nil))
现在举一个例子:

CL-USER 45 > (setf a6 (make-instance 'automobile :company :audi :motor :v6))
#<AUTOMOBILE 402005B47B>
让我们写入一个不存在但将添加到属性列表中的插槽:

CL-USER 47 > (setf (slot-value a6 'seats) 4)
4
我们可以收回价值:

CL-USER 48 > (slot-value c1 'seats)
4

我认为这值得单独回复,而不是发表评论,因此:

有时,当您认为您需要一个结构或对象,但这些实体无法满足某些特殊要求时,这可能是因为您实际需要的是一些不同的数据结构?当满足某些条件时,对象或结构是好的,其中一个条件是插槽是静态已知的-这允许编译器更好地解释代码,这对于优化和错误报告都是好的

另一方面,还有数据结构。其中一些提供了语言标准库,另一些则添加到语言标准库之上。这里有一个库提供了其中的许多功能:但对于特殊情况,还有更多功能

现在,我认为使用一种结构,比如列表、数组、某种树等,比尝试扩展对象或结构以允许动态添加插槽要好。这是因为我们通常期望访问插槽的时间可以忽略不计。也就是说,我们期望它是O(1)。无论对象有多少个插槽,这通常都会发生。现在,当您使用下面的列表时,您将其设为O(n),同时保持相同的语义!当然,您可以使用哈希表将其设置为O(1)(虽然这通常比插槽访问速度慢),但您将有一些其他意外行为,例如当插槽不存在时返回的
nil
,而不是常规错误等


我不认为以这种方式扩展对象在CL中是常见的做法,这可能就是为什么其他响应不会阻止您这样做的原因。与其他受访者相比,我对CL的了解较少,但我对这种用另一种语言进行的操纵感到非常难过,这种操纵很常见,而且通常令人沮丧。

谢谢,这几乎正是我想要的。但是,有没有办法像您所说的那样,让属性列表与
节点中的其他插槽一样?换句话说,有没有一种方法可以代替
(some-label(:one 1:two 2))
(使节点'some-label:one 1:two 2)=>(some-label:one 1:two 2)
)来执行
(使节点'some-label:one 1:two 2)=>(some-label:one:two 2)
?简言之,没有。一般来说,对结构进行了更深入的研究,但关键是结构具有固定数量的插槽。即使您可以使用
defstruct
来创建带有
(:type list)
选项的“穷人记录”,您仍然只有固定数量的插槽(这意味着列表具有固定数量的元素)。听起来你只是想要一份财产清单。是的,仔细阅读雷纳的答案,我认为这比我现在所做的需要更多的火力。谢谢你们的建议,伙计们!