Page 1 of 1

List building

Posted: Sat Sep 17, 2011 4:56 am
by pjstirling
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:

Code: Select all

`(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

Posted: Sun Sep 18, 2011 6:38 am
by adam33147
A small modification to function in "ANSI Common Lisp".

Code: Select all

(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

Posted: Mon Sep 19, 2011 5:30 pm
by Konfusius
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

Posted: Thu Sep 22, 2011 1:11 am
by marcoxa
adam33147 wrote:A small modification to function in "ANSI Common Lisp".

Code: Select all

(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.

Code: Select all

(defun filter (list &optional (test #'identity))
    (remove-if (complement test) list))
MA

Re: List building

Posted: Tue Oct 04, 2011 5:20 pm
by smithzv
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:

Code: Select all

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:

Code: Select all

(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

Posted: Thu Nov 03, 2011 2:55 am
by bass-machine
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:

Code: Select all

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?