Page 1 of 1

Another question about associate list

Posted: Thu Aug 26, 2010 12:33 pm
by joybee
I have create an associate list
mylist (list (list :a a)
(list :b b)
(list :c c))

later I need to check if the list contains an entry which has same value as a variable specified and if it does, then I want to delete the entry.

First I used member and it returned nil, why?
(dolist (myvar varlist)
(if (member myvar mylist) (setq mylist (delete myvar mylist))))

Then I took member function out and call delete directly, it still not working even though value at all the fields looked the same. Why?
(dolist (myvar varlist) (setq mylist (delete myvar mylist)))

Here is what I did, which is working:
(dolist (myvar varlist)
(dolist (elt mylist)
(if (equal myvar elt) (setq mylist (delete elt mylist)))
)
)
Is there another way to do it without using second dolist loop?


Puzzled!!!


Thanks!

Re: Another question about associate list

Posted: Thu Aug 26, 2010 10:23 pm
by Paul Donnelly
I think your problem is that you're forgetting the format of your alist. I don't know what varlist is, so I can't be sure.

Code: Select all

:b ≠ (:b b)
Keep in mind that any comparison you make will need to examine the first member of each alist entry, not the entry itself. You may find it easier to debug if you use remove instead of delete, and don't change mylist's binding. If you mess with your input data, you can easily get bad results when you test your function, because the input data isn't what you thought.

Re: Another question about associate list

Posted: Thu Aug 26, 2010 11:37 pm
by edgar-rft
If you see the lists as sets you can use 'set-difference':

Code: Select all

(defvar *mylist* '((:a a) (:b b) (:c c) (:d d)))

(let* ((first-var  'c)
       (second-var 'd)
       (third-var  'e)
       (fourth-var 'f)
       (varlist (list first-var second-var third-var fourth-var)))
  (setq *mylist* 
        (set-difference *mylist* varlist 
                        :test (lambda (mylist-arg varlist-arg)
                                (eq (second mylist-arg) varlist-arg)))))

*mylist* => ((:a a) (:b b))
The 'eq' test-function inside the lambda construct must be adapted to the exact data types of the variables inside the 'varlist' and the 'value' fields of *mylist*. It must be a function that produces a reasonable result and no error if comparing both values.

Re: Another question about associate list

Posted: Fri Aug 27, 2010 10:18 am
by joybee
The varlist is the same structure as mylist.

Re: Another question about associate list

Posted: Fri Aug 27, 2010 11:32 am
by edgar-rft

Code: Select all

(defvar *mylist*  (list (list :a 'a) (list :b 'b) (list :c 'c) (list :d 'd)))
(defvar *varlist* (list (list :c 'c) (list :d 'd) (list :e 'e) (list :f 'f)))

(setq *mylist*
      (set-difference *mylist* *varlist*
                      :test (lambda (mylist-arg varlist-arg)
                              (eq (second mylist-arg) (second varlist-arg)))))

*mylist* => ((:a a) (:b b))

Re: Another question about associate list

Posted: Thu Sep 02, 2010 7:24 pm
by audwinc
joybee:

Your problem is that you were not doing a list comparison; you were doing an address comparison. The (list :a 'a) in *mylist* is NOT the same (list :a 'a) in *varlist* if you compare with eq. If you compare with equal, you will compare the elements. By default, member uses eq to compare. You need to specify the :test for delete as well.

Code: Select all

(defvar *mylist*  (list (list :a 'a) (list :b 'b) (list :c 'c) (list :d 'd)))
(defvar *varlist* (list (list :c 'c) (list :d 'd) (list :e 'e) (list :f 'f)))

(dolist (myvar *varlist*)
  (delete myvar *mylist* :test #'equal)) => ((:a 'a) (:b 'b))

;; alternative
(setf *mylist*
  (set-difference *mylist* *varlist* :test #'equal) =>
          ((:a 'a) (:b 'b))
             or
          ((:b 'b) (:a 'a))
The caveat with set-difference is that it treats your list like a set, and sets do not care about the order or frequency of their elements.