I'm trying to define a structure with some properties I know I want, and an arbitrary number of other properties that aren't necessary for the basic structure.
(defstruct (node (:type list)) label [other args here])
I know in a function you can do:
(defun foo (arg1 &rest args) ...)
Is there some sort of &rest
equivalent for defstruct
?
I'm just learning lisp, so I have a feeling that I'm missing something. If there isn't an &rest
equivalent, any ideas on how I might go about this? Thanks in advance!
A structure is a Lisp object containing some number of slots, each of which can hold any Lisp data object. Functions are provided for accessing and setting the slots, creating or copying structure objects, and recognizing objects of a particular structure type.
setf is actually a macro that examines an access form and produces a call to the corresponding update function. Given the existence of setf in Common Lisp, it is not necessary to have setq, rplaca, and set; they are redundant. They are retained in Common Lisp because of their historical importance in Lisp.
What are arguments? Lisp programming is all about defining functions. Functions are general-purpose chunks of a program, and the arguments to a function are the specific data that you ask the function to deal with when you call the function.
The let expression is a special form in Lisp that you will need to use in most function definitions. let is used to attach or bind a symbol to a value in such a way that the Lisp interpreter will not confuse the variable with a variable of the same name that is not part of the function.
In Common Lisp, structures are thought as rigid and low-level records. They don't have fancy dynamic features.
What you can do with structures is to defined a new structure type which inherits from another one. There is single inheritance available.
To handle dynamic extensibility, a typical way is to add a property list slot to a structure. See the answer of Joshua.
Then there is the Common Lisp Object System, which provides multiple inheritance and you can change classes at runtime. So you can add a slot to a class and the instances of that class update themselves. You can also change the class of an object and slots may get added or deleted. Still, typically all instances of a class will have the same set of slots. Again, one sees that a slot with a property list can be added and used for extensibility.
There are other object systems for Common Lisp, which can easily add slots on a per-instance base. But it is usually too much to use them just for that, since they are quite a bit more powerful.
With CLOS and the Meta-object protocol one can try to hide it. Here I am using LispWorks:
We define a mixin class for our properties:
(defclass property-mixin ()
((plist :initform nil))
#+lispworks
(:optimize-slot-access nil))
Setting and reading the properties:
(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))
Now we write methods to make SLOT-VALUE
accepting our property names:
(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)))
Example. We define an automobile class with two slots:
(defclass automobile (property-mixin)
((company :initarg :company)
(motor :initarg :motor))
#+lispworks
(:optimize-slot-access nil))
Now an instance:
CL-USER 45 > (setf a6 (make-instance 'automobile :company :audi :motor :v6))
#<AUTOMOBILE 402005B47B>
We can get a normal slot value:
CL-USER 46 > (slot-value c1 'motor)
:V6
Let's write to a slot which does not exist, but will be added to our property list:
CL-USER 47 > (setf (slot-value a6 'seats) 4)
4
We can get the value back:
CL-USER 48 > (slot-value c1 'seats)
4
It's not clear exactly what you're looking for. The default case for structures is a record type with a fixed number of slots, each of which has a name, and is accessible through a function generated by the defstruct
macro. E.g., once you've done
(defstruct node
label)
you can access a node
's label with node-label
and get fast lookup time (since it's typically just an index into a memory chunk). Now, as you're doing, you can opt to use lists as the implementation of structures, in which case node-label
is just an alias for car
or 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))
If you're looking for arbitrary list-based key value pairs, you probably want a property list, for which Common Lisp contains some convenience functions.
If you want to have a structure that also contains a property list, you could add a special constructor populate that list. For instance,
(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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With