This is a read-only archive of lispforum.com. The forum was locked to new users and posts and is preserved here as static HTML from a database snapshot taken on 2019-09-07.

List building

6 posts · 2668 views

Something that comes up for me periodically is the need to build a list where either a value should appear or nothing (i.e. not nil)

So far my best stab is:
`(foo 
  bar
  ,@(when baz
     (list baz))
  quux)
But building a dummy list to splice in seems ugly. Does anyone have suggestions (or have I missed something simple)?

You can't use (REMOVE-IF #'NULL list) if there are necessary NILs in other parts of the list, and the ugliness of building complicated list structure via CONS and APPEND is the reason we have backquote in the first place.

Re: List building

A small modification to function in "ANSI Common Lisp".
(defun filter (lst &optional (fn #'identity))
	   (let ((acc nil))
	     (dolist (x lst)
	       (let ((val (funcall fn x)))
		 (if val (push val acc))))
	     (nreverse acc)))

Re: List building

I don't see the uglyness. An extra cons cell to store the extra list element is needed anyway. If you want to avoid unnessecary consing use ,. instead of ,@. ,. splices in the list destructively without extra consing. Or use `(,baz) instead of (list baz) to make the expression more consitent.

Re: List building

adam33147 wrote:A small modification to function in "ANSI Common Lisp".
(defun filter (lst &optional (fn #'identity))
	   (let ((acc nil))
	     (dolist (x lst)
	       (let ((val (funcall fn x)))
		 (if val (push val acc))))
	     (nreverse acc)))
Nahhh.
(defun filter (list &optional (test #'identity))
    (remove-if (complement test) list))
MA
Marco Antoniotti

Re: List building

I see that this is pretty old, but it needs saying. Be wary of using quasiquotes for building lists as they basically assume that any code that touches those lists isn't going to mutate them. In general, you cannot assume that everybody that uses your library is going know not to do that. Take for instance:
CL-USER> (defun func1 () `(1 2 ,@'(3 4)))
FUNC1
CL-USER> (func1)
(1 2 3 4)
CL-USER> (setf (cdr (func1)) '(a b c))
(A B C)
CL-USER> (func1)
(1 A B C)
CL-USER> (defun func2 () `(1 2 ,.'(3 4)))
FUNC2
CL-USER> (func2)
(1 2 3 4)
CL-USER> (setf (cdr (func2)) '(a b c))
(A B C)
CL-USER> (func2)
(1 A B C)
That list is closed over, not newly generated.

To answer your question, I find myself doing a lot of this:
(append '(a b c)
        (if (= x 5)
            (list 'd 'e 'f)
            () )
        ;; or
        (when (predicate?)
          (list 'h 'i 'j) ))
...or you can use NCONC if all of the lists are freshly consed.

Re: List building

smithzv wrote:I see that this is pretty old, but it needs saying. Be wary of using quasiquotes for building lists as they basically assume that any code that touches those lists isn't going to mutate them. In general, you cannot assume that everybody that uses your library is going know not to do that. Take for instance:
CL-USER> (defun func1 () `(1 2 ,@'(3 4)))
FUNC1
CL-USER> (func1)
(1 2 3 4)
CL-USER> (setf (cdr (func1)) '(a b c))
(A B C)
CL-USER> (func1)
(1 A B C)
I get in SBCL (1 A B C), but CCL (Clozure) says: (1 2 3 4). Do you know why?
regards
(bass-machine)