common-lispansi-common-lisp

(rassoc) returns NIL on nested list when (find) does not


I want to perform (rassoc) over a nested list like this:

(setq mlist
   (list (list (cons "A"  0)
               (make-hash-table))
         (list (cons "B"  1)
               (make-hash-table))))

Oddly enough, (find) and (assoc) work as expected, but (rassoc) fails.

(format t "~A~%" (assoc "A" mlist :key #'car :test #'equal))
(format t "~A~%" (find 0 mlist :key #'cdar :test #'equal))
(format t "~A~%" (rassoc 0 mlist :key #'car :test #'equal))

The Common Lisp HyperSpec states:

The expressions

(rassoc item list :test fn)

and

(find item list :test fn :key #'cdr)

are equivalent in meaning, except when the item is nil nd nil appears in place of a pair in the alist. See the function assoc.

So, given this, I wonder, is this a bug or am I just using (rassoc) incorrectly? I'm using sbcl 2.4.0


Solution

  • You are using rassoc incorrectly. rassoc looks at the cdrs of the entries in an alist, skipping nil entries, and compares them (or the result of applying the key function to them) with what you want.

    The cdrs of the entries in your list are single-element lists containing a hashtable, with a key of #'car you will be comparing against those hashtables.

    You shouldn't be using rassoc at all if what you want to do is to check against the second element of the cars of the alist. Instead use assoc with an appropriate key:

    > (assoc "A" mlist :key #'car :test #'equal)
    (("A" . 0) #<eql Hash Table{0} 82200283A3>)
    
    > (assoc 0 mlist :key #'cdr)
    (("A" . 0) #<eql Hash Table{0} 82200283A3>)