Reputation: 3181
Let's say I have a struct (which has more parameters than shown here):
(defstruct location
name)
And an association list defining a series of locations using the backquote:
(defparameter *locations* `(
(ASIA ,(make-location :name "Asia"))
(AFRICA ,(make-location :name "Africa"))
)
This works fine and correctly creates the location structs. The problem is, I'm planning on having a lot of locations, more than would be nice to type out all by hand:
(defparameter *locations* `(
(ASIA ,(make-location :name "Asia"))
(AFRICA ,(make-location :name "Africa"))
(LOC1 ,(make-location :name "Location 1"))
; Lots more...
(LOC1024 ,(make-location :name "Location 1027"))
)
A lot of these extra locations have similar parameters, such that I can define a "generator function" to create a list of them:
(defun generate-locations (symbol name count)
(loop for i from 1 to count collect (list
(read-from-string (format nil "~A~D" symbol i))
(make-location :name name))))
;;; Creates list ((LOC1 #S(LOCATION :NAME "Location 1")) (LOC2 ...
(generate-locations "LOC" "Location " 1024)
So then I tried to do something like:
(defparameter *locations* `(
(ASIA ,(make-location :name "Asia"))
(AFRICA ,(make-location :name "Africa"))
,(generate-locations "LOC" "Location " 1024)
)
Doesn't work, because GENERATE-LOCATIONS
returns a list, not a series of elements which can then be added to a list. So I tried to VALUES-LIST
it:
(defparameter *locations* `(
(ASIA ,(make-location :name "Asia"))
(AFRICA ,(make-location :name "Africa"))
,(values-list (generate-locations "LOC" "Location " 1024))
)
This only adds the first generated location to *LOCATIONS*
. I'm assuming this is because all return values except the first of VALUES-LIST
are ignored.
So how do I correctly add a series of elements to *LOCATIONS*
? Should GENERATE-LOCATIONS
be a macro? And if so, how would it be structured?
Upvotes: 3
Views: 143
Reputation: 2600
You need to use the splicing unquote operator, ,@
. In your example:
(defparameter *locations* `(
(ASIA ,(make-location :name "Asia"))
(AFRICA ,(make-location :name "Africa"))
,@(generate-locations "LOC" "Location " 1024)
)
For this to work, generate-locations
should return a list. ,@
will splice each item into the surrounding list, rather than inserting it as a single item.
Upvotes: 7