The Good Thing
The code works and does what was intended. Making it shorter and improving readability are improvements not bug fixes.
It is hard to follow the logic of the code. Adding a documentation string and comments would assist a future programmer (which might be the program's author in a few months) in figuring out what is going on and guide the programmer in making changes.
Names The name
modulename-vinlist does not follow traditional naming conventions for Lisp programs. Hyphen case is more typical
module-name-v-in-list would be more conventional. I'd probably favor adding a few extra keystrokes for legibility with
module-name-value-in-list because it is more readable and often spend longer reading code than writing it.
As noted in the accepted answer Emacs Lisp provides the
member function as a simple way of determining if a value is in a list. However, it is possible to mean different things by 'value' depending on context. Emacs Lisp addresses this with additional functions such as
memql etc. to provide more stringent set semantics for lists. Emacs Lisp also provides a more general
member-ignore case for contexts where strings matter.
For example working with string values:
ELISP> (member "PRINTED VALUE" '("unprinted value" "printed value"))
ELISP> (member-ignore-case "PRINTED VALUE" '("unprinted value" "printed value"))
Or based on strict identity with
ELSIP> (let ((a 'b))
(memq a '(c b d)))
But there is not a function for working with numerical values of different types:
ELISP> (member '2.0 '(1 2 3))
To address this last class of cases means going even further, and by 'further' I mean looking outside of Emacs Lisp. ANSI Common Lisp implements it's
member function to take an arbitrary test for equality and this is the sort of thing that might be useful.
And since we're already writing a function...how hard could it be to add that flexibility?
An Implementation of value-in-list
Like most questions that start with "How hard could it be" it turns out that it is not as easy as was initially thought. In this case because there appears to be a 'bug' like behavior as documented in the comments.
(defun value-in-list (value list &optional test)
"Uses test to determine if value is in list.
If value is in in list, returns the cdr of the list
which begins with value: the return value mimics the behavior of MEMBER.
Example: (my-member 3.0 '(1 2 3) '=) -> (3)
(my-member '(1) '((1) (2) (3)) 'eq) -> nil
Remarks: This function could in theory be structured as the local function
'my-member, but Emacs-Lisp runs out of stack space if 'eq or 'eql are passed
to 'my-member within another function. Seems like a bug."
((my-member (lambda (value list test)
(if (funcall test value (car list))
(my-member value (cdr list) test)))))
(member value list))
((eq test 'eq)
(memq value list))
((eq test 'eql)
(memql value list))
(t (funcall my-member value list test)))))
Note the use
funcall in two places where a symbol bound to a function is passed as a parameter to another function:
- Within the locally defined function
- In the call to
my-member in the
t condition of the
This is necessary within Emacs Lisp and one of the ways it varies from Common Lisp and various Schemes.
Example Use Despite the difficulties that add complexity,
value-in-list provides a great deal of flexibility:
ELISP> (value-in-list 2 '(1 2 3))
ELISP> (value-in-list '(2) '(1 (2) 3))
ELISP> (value-in-list 1 '(1 2 3) #'(lambda (x y) (< x y)))
ELISP> (value-in-list 2.0 '(1 2 3) '=)
ELISP> (value-in-list 2
'(1 (2) 3)
#'(lambda (x y) (if (consp y)
(= x (car y)))))
Shorter is so long as it does not come at the cost of power. Writing one's own version of a function provides an opportunity to make a pneumatic tire along with the risk of reinventing the wheel.