Question

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?

Was it helpful?

Solution

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top