patternMinor
Checking if val is present in list
Viewed 0 times
presentlistcheckingval
Problem
I pretty sure it should be shoter solution.
(defun modulename-vinlist(val lst)
"Check if value in list"
(let ((res "no"))
(let (vv)
(dolist (pi lst vv)
(when (string-equal pi val)
(setq res "yes")
(return))
(setq vv (cons pi vv))))
(when (string-equal res "yes")
t)))Solution
The Good Thing
The code works and does what was intended. Making it shorter and improving readability are improvements not bug fixes.
Readability
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
Philosophy
As noted in the accepted answer Emacs Lisp provides the
For example working with string values:
Or based on strict identity with
But there is not a function for working with numerical values of different types:
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
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.
Highlights
Note the use
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,
Final Thoughts
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.
The code works and does what was intended. Making it shorter and improving readability are improvements not bug fixes.
Readability
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.Philosophy
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 memq, 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"))
nil
ELISP> (member-ignore-case "PRINTED VALUE" '("unprinted value" "printed value"))
("printed value")Or based on strict identity with
memq:ELSIP> (let ((a 'b))
(memq a '(c b d)))
(b d)But there is not a function for working with numerical values of different types:
ELISP> (member '2.0 '(1 2 3))
nilTo 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."
(let
((my-member (lambda (value list test)
(if (funcall test value (car list))
list
(my-member value (cdr list) test)))))
(cond
((null test)
(member value list))
((eq test 'eq)
(memq value list))
((eq test 'eql)
(memql value list))
(t (funcall my-member value list test)))))Highlights
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
my-member.
- In the call to
my-memberin thetcondition of thecondexpression.
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))
(2 3)
ELISP> (value-in-list '(2) '(1 (2) 3))
((2)
3)
ELISP> (value-in-list 1 '(1 2 3) #'(lambda (x y) ( (value-in-list 2.0 '(1 2 3) '=)
(2 3)
ELISP> (value-in-list 2
'(1 (2) 3)
#'(lambda (x y) (if (consp y)
(= x (car y)))))
((2)
3)Final Thoughts
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.
Code Snippets
ELISP> (member "PRINTED VALUE" '("unprinted value" "printed value"))
nil
ELISP> (member-ignore-case "PRINTED VALUE" '("unprinted value" "printed value"))
("printed value")ELSIP> (let ((a 'b))
(memq a '(c b d)))
(b d)ELISP> (member '2.0 '(1 2 3))
nil(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."
(let
((my-member (lambda (value list test)
(if (funcall test value (car list))
list
(my-member value (cdr list) test)))))
(cond
((null test)
(member value list))
((eq test 'eq)
(memq value list))
((eq test 'eql)
(memql value list))
(t (funcall my-member value list test)))))ELISP> (value-in-list 2 '(1 2 3))
(2 3)
ELISP> (value-in-list '(2) '(1 (2) 3))
((2)
3)
ELISP> (value-in-list 1 '(1 2 3) #'(lambda (x y) (< x y)))
(2 3)
ELISP> (value-in-list 2.0 '(1 2 3) '=)
(2 3)
ELISP> (value-in-list 2
'(1 (2) 3)
#'(lambda (x y) (if (consp y)
(= x (car y)))))
((2)
3)Context
StackExchange Code Review Q#146011, answer score: 2
Revisions (0)
No revisions yet.