Tests for 'assoc' and 'member'


#1

If you consider the Common Lisp spec, both assoc and member provide a test predicate for searching.
This compliments the various tests for equality which uLisp expects elsewhere.

Off the top of my head we’ve got eq, equal, and string=. People could provide a custom test in the case they wanted to compare structures which were equal in some other higher order way. For example if you had instances with a unique id but only wanted to compare their “type” contained in a record for equality.


#3

You can define them yourself very easily. For example:

member

(defun member* (a lis test)
  (cond
   ((null lis) nil)
   ((funcall test a (car lis)) lis)
   (t (member* a (cdr lis) test))))

> (member* "a" '("c" "b" "a" "b" "e") #'string=)
("a" "b" "e")

> (member* "d" '("c" "b" "a" "b" "e") #'string=)
nil

assoc

(defun assoc* (a alist test)
  (cond
   ((null alist) nil)
   ((funcall test a (caar alist)) (car alist))
   (t (assoc* a (cdr alist) test))))

> (assoc* "a" '(("b" . 2) ("a" . 3) ("c" . 4)) #'string=)
("a" . 3)

> (assoc* "d" '(("b" . 2) ("a" . 3) ("c" . 4)) #'string=)
NIL

#4

Yeah, these forms would be closer to the CL standard for sure.
Not too bad to implement as shown.

How would you feel about replacing the originals with an optional keyword that defaults to eq if not provided?
I think it’d be a non breaking change for folks with existing code, but would also give a lot of power closer to the spec.

Essentially the value for :test would be (or test 'eq) -> giving test if test was defined otherwise 'eq.
These are good solutions. I do find myself using the tests on occasion in CL because the comparison model has quite a few notions of equality.


#5

One of my design aims of uLisp is to keep it as slim as possible, so I’m trying to resist the temptation to add new features, especially if it’s easy to add them in Lisp.

A version of uLisp still runs on the original platform I targeted: the Arduino Uno, with 32 KBytes of RAM.

The Common Lisp spec is huge, and not supporting keyword arguments is a good dividing line; if I add :test someone will ask for :test-not, :key, etc.


#6

Ok, that’s fair enough. :)